我的可扩展listview
滚动非常慢,当我点击父类别时我需要一段时间,直到看到child
视图。
组列表活动:
public class GroupsListActivity extends Activity {
String loggedUserId = Model.getInstance().getLoggedUserId();
List<String> groupsList;
static ExpandableListView expandableListView;
HashMap<String, List<Group>> groupCategories = new HashMap<String, List<Group>>();
static ProgressBar spinner;
static TextView textLoading;
ImageButton createCategoryButton;
static Adapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout for this activity
setContentView(R.layout.expandable_list);
// Set actionbar title
getActionBar().show();
getActionBar().setTitle(Html.fromHtml("<font color='#fffffff'>Groups</font>"));
if (loggedUserId != null)
Log.d("TAG", "My Groups for user ID: " + loggedUserId);
// Connect between buttons to layout id
expandableListView = (ExpandableListView) findViewById(R.id.exp_list);
spinner = (ProgressBar) findViewById(R.id.spinner);
createCategoryButton = (ImageButton) findViewById(R.id.createCategory);
textLoading = (TextView) findViewById(R.id.textLoading);
// Loading data to expandable group list asynchronously
AsyncTask<String, String, HashMap<String, List<Group>>> task = new AsyncTask<String, String, HashMap<String, List<Group>>>() {
@Override
protected HashMap<String, List<Group>> doInBackground(String... params) {
return DataProvider.getInfo();
}
@Override
protected void onPostExecute(HashMap<String, List<Group>> listHashMap) {
super.onPostExecute(listHashMap);
// Setting adapter and creating group list
groupCategories = listHashMap;
groupsList = new ArrayList<String>(groupCategories.keySet());
adapter = new Adapter(GroupsListActivity.this, groupCategories, groupsList, GroupsListActivity.this);
expandableListView.setAdapter(adapter);
// Hide spinner after loading
spinner.setVisibility(View.GONE);
textLoading.setVisibility(View.GONE);
}
};
task.execute();
// Setting listener for group click
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int parentPosition, int childPosition, long id) {
// After selecting a group on row - open contacts list for this group
expandableListView.setEnabled(false);
openContactListForGroup(groupCategories.get(groupsList.get(parentPosition)).get(childPosition).getGroupID());
return true;
}
});
// Setting listener for create group click
createCategoryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createCategoryButton.setEnabled(false);
onCategoryCreate(GroupsListActivity.this, createCategoryButton);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_actionbar_groups, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_create:
onCreate();
return true;
case R.id.action_search:
onSearch();
return true;
case R.id.action_favorites:
onFavorites();
return true;
case R.id.action_settings:
onSettings();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// Menu methods
private void onCreate() {
Log.d("TAG", "Create button was pressed");
Intent i = new
Intent(getApplicationContext(),
CreateGroupActivity.class);
startActivity(i);
overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
}
private void onSearch() {
Log.d("TAG", "Search button was pressed");
Intent i = new
Intent(getApplicationContext(),
SearchActivity.class);
startActivity(i);
overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
}
private void onFavorites() {
Log.d("TAG", "Favorites button was pressed");
Intent i = new
Intent(getApplicationContext(),
FavoritesListActivity.class);
startActivity(i);
overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
}
private void onSettings() {
Log.d("TAG", "Settings button was pressed");
// Settings activity
Intent i = new
Intent(getApplicationContext(),
SettingsActivity.class);
startActivity(i);
overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
}
// Methods to handle action buttons
private void onCategoryCreate(final Activity activity, final ImageButton createCategoryButton) {
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
createCategoryButton.setEnabled(true);
final String title = "Create a new category";
String message = "Type a name for your new category";
// Set dialog edit_text
final EditText categoryNameTextView = new EditText(activity);
categoryNameTextView.setHint("Type your category name");
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
categoryNameTextView.setLayoutParams(lp);
builder.setView(categoryNameTextView);
// Set dialog title and message
if (title != null)
builder.setTitle(Html.fromHtml("<font color='#dc1c1c'>" + title + "</font>")).setMessage(message);
// Set dialog buttons
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final String newCategoryName = categoryNameTextView.getText().toString();
// Check if contains only spaces
if (!(newCategoryName.trim().length() > 0))
Toast.makeText(activity, "Type at least 1 letter to create the category", Toast.LENGTH_LONG).show();
// Check if category name already exists
else if (groupsList.contains(newCategoryName))
Toast.makeText(activity, newCategoryName + " already exist. Please type another category name", Toast.LENGTH_LONG).show();
else {
// Create a new category in server and add user to a sample group
adapter.getCategoriesList().add(newCategoryName);
adapter.getGroupsList().put(newCategoryName, Collections.<Group>emptyList());
// Update adapter and show toast to user
GroupsListActivity.updateAdapter();
Toast.makeText(activity, "You created " + newCategoryName + " category", Toast.LENGTH_LONG).show();
}
}
});
builder.setNegativeButton(
"Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.setIcon(R.mipmap.edit);
builder.show();
}
public void openContactListForGroup(String groupId) {
// Contacts List activity
Intent i = new
Intent(getApplicationContext(),
ContactsListActivity.class);
// Pass to details activity the logged group id and start activity
Bundle b = new Bundle();
b.putString("groupId", groupId);
i.putExtras(b);
startActivity(i);
overridePendingTransition(R.animator.slide_out_right, R.animator.slide_in_right);
}
// Static methods to use from other activities
public static void updateAdapter() {
spinner.setVisibility(View.VISIBLE);
adapter.notifyDataSetChanged();
// Hide spinner after adapter finish the update
expandableListView.post(new Runnable() {
@Override
public void run() {
spinner.setVisibility(View.GONE);
}
});
}
public static void addGroupToList(String groupId) {
Model.getInstance().getGroup(groupId, new Model.groupReturnedListener() {
@Override
public void addGroupToLocal(Group group) {
// Add group to category Others in Group List Activity
if (adapter.getGroupsList().get("Others").size() == 0) {
// Add group to empty list
List<Group> list = new LinkedList<Group>();
list.add(group);
adapter.getGroupsList().put("Others", list);
adapter.notifyDataSetChanged();
} else {
// Add group to an existing list
adapter.getGroupsList().get("Others").add(group);
adapter.notifyDataSetChanged();
}
}
});
}
public static void removeGroupFromList(String groupId) {
int position = -1;
// Get category position
String oldCategoryName = Model.getInstance().getCategoryNameByGroupId(groupId);
List<Group> data = adapter.getGroupsList().get(oldCategoryName);
// Search for group position
for (Group group : data) {
if (group.getGroupID().equals(groupId)) {
position = data.indexOf(group);
break;
}
}
// Groups was found
if (position != -1) {
data.remove(position);
adapter.notifyDataSetChanged();
}
}
public static void updateGroupFromList(Group group) {
int position = -1;
// Get category position
String oldCategoryName = Model.getInstance().getCategoryNameByGroupId(group.getGroupID());
List<Group> data = adapter.getGroupsList().get(oldCategoryName);
// Search for group position
for (Group groupIterator : data) {
if (groupIterator.getGroupID().equals(group.getGroupID())) {
position = data.indexOf(groupIterator);
break;
}
}
// Groups was found
if (position != -1) {
data.remove(position);
data.add(group);
adapter.notifyDataSetChanged();
}
}
// Other methods
@Override
protected void onResume() {
super.onResume();
expandableListView.setEnabled(true);
}
@Override
public void onBackPressed() {
ExitDialog exitDialog = new ExitDialog(GroupsListActivity.this);
exitDialog.show();
}
}
这是适配器的活动,我在其中加载了可展开的listview
:
@Override
public View getGroupView(final int parent, boolean isExpanded, View convertView, ViewGroup parentView) {
final String categoryName = (String)getGroup(parent);
ParentViewHolder pHolder = null;
if(convertView == null) {
pHolder = new ParentViewHolder();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.expandable_list_parent, parentView, false);
// Connect between buttons to layout id
pHolder.categoryNameTextView = (TextView) convertView.findViewById(R.id.categoryName);
pHolder.editCategory = (ImageButton) convertView.findViewById(R.id.editCategory);
pHolder.deleteCategory = (ImageButton) convertView.findViewById(R.id.deleteCategory);
convertView.setTag(pHolder);
}
else {
pHolder = (ParentViewHolder) convertView.getTag();
}
// Hide edit and delete button for category name Others
if(categoriesList.get(parent).equals("Others")){
pHolder.editCategory.setVisibility(View.GONE);
pHolder.deleteCategory.setVisibility(View.GONE);
}
else {
pHolder.editCategory.setVisibility(View.VISIBLE);
pHolder.deleteCategory.setVisibility(View.VISIBLE);
}
// Set category name on row
pHolder.categoryNameTextView.setTypeface(null, Typeface.BOLD);
pHolder.categoryNameTextView.setText(categoryName + ": " + getChildrenCount(parent));
// Set edit category button listener
final ParentViewHolder finalPHolder = pHolder;
pHolder.editCategory.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalPHolder.editCategory.setEnabled(false);
editCategoryName(activity, finalPHolder.categoryNameTextView.getText().toString().toString().split(": ")[0], finalPHolder.editCategory, parent);
}
});
// Set delete category button listener
pHolder.deleteCategory.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalPHolder.deleteCategory.setEnabled(false);
deleteCategory(activity, categoryName, finalPHolder.deleteCategory);
}
});
return convertView;
}
@Override
public View getChildView(final int parent, final int child, boolean lastChild, View convertView, ViewGroup parentView) {
final Group group = (Group)getChild(parent, child);
ChildViewHolder cHolder = null;
if(convertView == null){
cHolder = new ChildViewHolder();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.expandable_list_child, parentView, false);
// Connect between buttons to layout id
cHolder.groupImage = (ImageView) convertView.findViewById(R.id.groupImage);
cHolder.groupName = (TextView) convertView.findViewById(R.id.groupName);
cHolder.moveCategory = (ImageButton) convertView.findViewById(R.id.moveCategory);
cHolder.groupFavoritesButton = (ImageButton) convertView.findViewById(R.id.groupFavorites);
cHolder.groupLeaveGroupButton = (Button) convertView.findViewById(R.id.groupLeave);
cHolder.groupImageProgressbar = (ProgressBar) convertView.findViewById(R.id.groupImageProgressBar);
convertView.setTag(cHolder);
} else {
cHolder = (ChildViewHolder) convertView.getTag();
}
// Set group name on row
cHolder.groupName.setText(group.getName());
// Load group image
cHolder.groupImageProgressbar.setVisibility(View.VISIBLE);
final ChildViewHolder finalHolder = cHolder;
Model.getInstance().getGroupImage(group.getImageName(), new Model.LoadImageListener() {
@Override
public void onResult(Bitmap imageBmp) {
finalHolder.groupImage.setImageBitmap(imageBmp);
finalHolder.groupImageProgressbar.setVisibility(View.GONE);
finalHolder.groupImage.setVisibility(View.VISIBLE);
}
});
// Set move category button listener
cHolder.moveCategory.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalHolder.moveCategory.setEnabled(false);
showDialogMoveCategory(activity, group.getGroupID(), finalHolder.moveCategory);
}
});
// After click on group image - open profile for this group
cHolder.groupImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onGroupSelected(group.getGroupID());
}
});
// Setting favorite Button Image
boolean isFavorite = Model.getInstance().groupIsFavorite(loggedUserId, group.getGroupID());
if(isFavorite)
cHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_on);
else
cHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_off);
// Setting favorite Button Action
cHolder.groupFavoritesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Add group to favorites
if (!Model.getInstance().groupIsFavorite(loggedUserId, group.getGroupID())) {
finalHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_on);
Toast.makeText(activity,
"The group " + group.getName() + " was added to favorites", Toast.LENGTH_SHORT).show();
Model.getInstance().changeFavoriteStatus(loggedUserId, group.getGroupID(), "true");
} else {
// Delete group from favorites
finalHolder.groupFavoritesButton.setBackgroundResource(R.mipmap.favorites_off);
Toast.makeText(activity,
"The group " + group.getName() + " was removed from favorites", Toast.LENGTH_SHORT).show();
Model.getInstance().changeFavoriteStatus(loggedUserId, group.getGroupID(), "false");
}
}
});
// After click on group action - leave group
cHolder.groupLeaveGroupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalHolder.groupLeaveGroupButton.setEnabled(false);
showDialogLeaveGroup(activity, "Are you sure ?", "This action will remove yourself from the group " + group.getName(), group.getGroupID(), parent, child);
finalHolder.groupLeaveGroupButton.setEnabled(true);
}
});
return convertView;
}
加载图片方法:
public void getGroupImage(final String imageName, final LoadImageListener listener) {
AsyncTask<String, String, Bitmap> task = new AsyncTask<String, String, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
Bitmap bmp = loadImageFromFile(imageName); //first try to find the image on the device
// Bitmap bmp = null;
if (bmp == null) { //if image not found - try downloading it from parse
bmp = modelParse.getGroupImage(imageName);
if (bmp != null)
saveImageToFile(bmp, imageName); //save the image locally for next time *****
}
Bitmap scaledBitmap = scaleDown(bmp, 200, true);
return scaledBitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
listener.onResult(result);
}
};
task.execute();
}
private void saveImageToFile(Bitmap imageBitmap, String imageFileName) {
FileOutputStream fos;
OutputStream out = null;
try {
File dir = context.getExternalFilesDir(null);
out = new FileOutputStream(new File(dir, imageFileName + ".jpg"));
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private Bitmap loadImageFromFile(String fileName) {
Bitmap bitmap = null;
try {
File dir = context.getExternalFilesDir(null);
InputStream inputStream = new FileInputStream(new File(dir, fileName + ".jpg"));
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bitmap;
}
答案 0 :(得分:0)
我看到两个主要的性能问题。
首先,你到处使用findViewById。你走的是整个视图树。这就是人们使用ViewHolder模式或自定义视图模式的原因。如果您在应用的生命周期中每次使用findViewById多次,那么您做错了。
其次,每次用户滚动时都会分配新对象。别。使用ViewHolder或vustom视图模式,以便您可以为所有滚动事件重用相同的OnClickListener,每行只创建一次并根据需要更新值。如果你曾经在你的getView中创建一个对象,除了当convertView为null时,你的效率非常低。
答案 1 :(得分:0)
我将尝试给出关于列表视图和适配器的一般(抽象)概念,这可以帮助您自己找出错误的部分。
适配器的整个目的是显示相应列表项的正确数据,并尽可能少地完成其他工作。在这个过程中,任何与数据操作相关的东西都需要cpu循环,这会导致滞后和慢滚动。
具体来说,Android应用程序应该以平滑的每秒60帧的速度运行,并且为了达到60FPS的速率,每帧应该不再需要16.6毫秒来渲染。因此,如果您为CPU创建额外的负载,可能会出现帧渲染的问题,从这里开始的渲染滞后路径很短。
我在说什么 - 你的适配器中可能有一些方法是在现场操作数据,同步并且它会对cpu造成负担。适配器应该代表ALREADY准备显示的数据,并在正确的视图中显示它。性能问题的一个示例可能就像每次为每个视图使用String.replace()方法一样简单,或者另一个坏示例将同步而不是异步加载图像。