Android:如果ListView的性能仍然不够,该怎么办?
这个话题过去和现在都有很多争论,我已经阅读了很多教程,提示和相关的讨论。但是,每当我的行达到一定的复杂性时,我在实现ListView的自定义BaseAdapter时仍然会遇到问题。 因此,我基本上拥有一些通过解析来自网络的xml得到的实体。此外,我还获取了一些图像等,所有这些都是在一个异步任务中完成的。 我在getView()方法中使用性能优化ViewHandler方法,并按照大家的建议重用convertView。也就是说,我希望我使用的是应该使用的ListView,当我只显示一个ImageView和两个TextView时,它真的工作得很好,这两个视图都使用了SpannableStringBuilder(我不使用任何HTML.fromHTML) 现在它来了。每当我用多个小图像视图、一个按钮和更多的文本视图扩展行布局时,我都会得到一个停止滚动的性能。该行由一个RelativeLayout作为父元素组成,所有其他元素都使用布局参数进行排列,因此我无法使该行的布局更简单。我必须承认,我从未见过任何一个ListView实现的示例,其中的行包含那么多UI元素 但是,当我在ScrollView中使用TableLayout并手动填充AsyncTask(onProgressUpdate()稳定添加的新行)时,即使其中有数百行,它的行为也非常平滑。如果滚动到列表的末尾,则在添加新行时会有一点出错。否则,它要比ListView平滑得多,在ListView中,滚动时它总是会出错 当ListView不想表现好时,有什么建议吗?我应该继续使用TableLayout方法,还是建议使用ListView来优化性能 以下是我的适配器的实现:Android:如果ListView的性能仍然不够,该怎么办?,android,performance,android-listview,tablelayout,baseadapter,Android,Performance,Android Listview,Tablelayout,Baseadapter,这个话题过去和现在都有很多争论,我已经阅读了很多教程,提示和相关的讨论。但是,每当我的行达到一定的复杂性时,我在实现ListView的自定义BaseAdapter时仍然会遇到问题。 因此,我基本上拥有一些通过解析来自网络的xml得到的实体。此外,我还获取了一些图像等,所有这些都是在一个异步任务中完成的。 我在getView()方法中使用性能优化ViewHandler方法,并按照大家的建议重用convertView。也就是说,我希望我使用的是应该使用的ListView,当我只显示一个ImageVi
protected class BlogsSeparatorAdapter extends BaseAdapter {
private LayoutInflater inflater;
private final int SEPERATOR = 0;
private final int BLOGELEMENT = 1;
public BlogsSeparatorAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return blogs.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
int type = BLOGELEMENT;
if (position == 0) {
type = SEPERATOR;
} else if (isSeparator(position)) {
type = SEPERATOR;
}
return type;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
UIBlog blog = getItem(position);
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.blogs_row_layout, null);
holder.usericon = (ImageView) convertView.findViewById(R.id.blogs_row_user_icon);
holder.title = (TextView) convertView.findViewById(R.id.blogs_row_title);
holder.date = (TextView) convertView.findViewById(R.id.blogs_row_date);
holder.amount = (TextView) convertView.findViewById(R.id.blogs_row_cmmts_amount);
holder.author = (TextView) convertView.findViewById(R.id.blogs_row_author);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.usericon.setImageBitmap(blog.icon);
holder.title.setText(blog.titleTxt);
holder.date.setText(blog.dateTxt);
holder.amount.setText(blog.amountTxt);
holder.author.setText(blog.authorTxt);
return convertView;
}
class ViewHolder {
TextView separator;
ImageView usericon;
TextView title;
TextView date;
TextView amount;
TextView author;
}
/**
* Check if the blog on the given position must be separated from the last blogs.
*
* @param position
* @return
*/
private boolean isSeparator(int position) {
boolean separator = false;
// check if the last blog was created on the same date as the current blog
if (DateUtility.getDay(
DateUtility.createCalendarFromUnixtime(blogs.get(position - 1).getUnixtime() * 1000L), 0)
.getTimeInMillis() > blogs.get(position).getUnixtime() * 1000L) {
// current blog was not created on the same date as the last blog --> separator necessary
separator = true;
}
return separator;
}
}
这是该行的xml(没有按钮,仍然有障碍):
为了清楚起见,我对这两个适配器都使用了这个选项。您应该使用DDMS的探查器来查看时间的确切位置。我怀疑您在getView()中所做的事情是昂贵的。例如,viewUtility.setUserIcon(holder.usericon,blogs.get(position).getUid(),30);每次创建一个新图标?一直对图像进行解码会导致打嗝。需要阅读的内容很多;) 我看不出你的布局有什么问题。 你可以优化 -你的第一个if是|| -缓存blogs.get(位置)在变量中 -德克莱尔:你是个静态的人。 -为什么要使用日历并将其装饰回ms?你好像已经有你的ms了 但我担心这还不够 问候,,
Stéphane那么您对表格有什么问题?您发现一个解决方案性能很好,但为什么您更喜欢列表?我当然对表没有任何问题,它实际上只是一个问题“使用ListView是否有任何限制?或者您是否可以始终使用ListView(尽管有它的优点)作为列表?”在优化ListView性能时,可能我在某种程度上使用ListView是错误的。:-)@user535762:使用Traceview来确定您的时间花在哪里。这两种方法之间的一个重要区别是,在ArrayAdapter版本中,您只调用一次get(position)。如果随机访问列表(LinkedList)的成本很高,那么这可能是一个重要的区别。另外,发布traceview的截图对我们来说也不是很有用。您的屏幕截图没有显示getView()中发生了什么,并且很难验证您的声明。这已经晚了,但基于BaseAdapter的原始适配器显然是错误的。getItem(位置)返回位置?使用ArrayAdapter时效果更好的原因可能是因为他正在将对象从他正在使用的任何东西(可能每次都会解析它们)转换为一个合适的支持数组。好的,我将使用探查器进行检查。对于你的问题:我只是在做view.setImageBitmap(myBitmap);位图已经被提取并存储在ArrayList中,所以我无法想象这应该是原因。当我在探查器中看到可疑内容时,我会将结果发布在这里。:)BaseAdapter什么都不做,它不会受到“性能差”的影响。当您使用探查器时,您在哪里看到了所花费的时间?查看BaseAdapter的源代码,您就会明白它必须来自您在getView()中所做的事情。它没有任何作用。请不要建议其他开发人员不要使用BaseAdapter,因为您的结论完全错误。您的分析图片无法证明任何东西,因为缺少重要数据。我在Android团队中维护BaseAdapter和ArrayAdapter,我对它们的工作原理有很好的了解。看看BaseAdapter做了什么:您的代码中一定有差异,例如,您正在使用的uiBlogs列表是什么?所以。。很抱歉对此进行了更改,但是您是否发现BaseAdapter导致性能下降的原因?只是出于兴趣。谢谢你,这是解决问题的办法我将告诉您为什么我使用Calendar:将给定的博客unixtime设置为特定的日期时间。getDay(cal,0)方法将给定的日历精确设置为cal给定日期的00:00:00。因此,如果cal是11月2日3:40,它将设置为11月2日00:00:00。这样我就可以检查博客是在这个确切的日期之前还是之后出现的。但正如你所说,这不可能是问题的原因。也许我在没有释放它们的情况下创建了太多的对象。我会检查一下……我仍然认为你可以优化更多的东西。例如,如我所说,使用变量缓存blogs.get(position)的结果。第二件要优化的事情是存储isSeparator的结果。您可以在UIBlog列表(或
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@drawable/listview_selector">
<ImageView
android:id="@+id/blogs_row_user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:paddingTop="@dimen/blogs_row_icon_padding_top"
android:paddingLeft="@dimen/blogs_row_icon_padding_left"/>
<TextView
android:id="@+id/blogs_row_title"
android:layout_toRightOf="@id/blogs_row_user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/blogs_row_title_padding"
android:textColor="@color/blogs_table_text_title"/>
<TextView
android:id="@+id/blogs_row_date"
android:layout_below="@id/blogs_row_title"
android:layout_toRightOf="@id/blogs_row_user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/blogs_row_date_padding_left"
android:textColor="@color/blogs_table_text_date"/>
<ImageView
android:id="@+id/blogs_row_cmmts_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/blogs_row_title"
android:layout_toRightOf="@id/blogs_row_date"
android:layout_margin="@dimen/blogs_row_cmmts_icon_margin"
android:src="@drawable/comments"/>
<TextView
android:id="@+id/blogs_row_cmmts_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/blogs_row_title"
android:layout_toRightOf="@id/blogs_row_cmmts_icon"
android:layout_margin="@dimen/blogs_row_author_margin"/>
<TextView
android:id="@+id/blogs_row_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/blogs_row_title"
android:layout_toRightOf="@id/blogs_row_cmmts_amount"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_margin="@dimen/blogs_row_author_margin"/>
</RelativeLayout>
protected class BlogsSeparatorAdapter extends ArrayAdapter<UIBlog> {
private LayoutInflater inflater;
private final int SEPERATOR = 0;
private final int BLOGELEMENT = 1;
public BlogsSeparatorAdapter(Context context, List<UIBlog> rows) {
super(context, R.layout.blogs_row_layout, rows);
inflater = LayoutInflater.from(context);
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
int type = BLOGELEMENT;
if (position == 0) {
type = SEPERATOR;
} else if (isSeparator(position)) {
type = SEPERATOR;
}
return type;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final UIBlog blog = uiblogs.get(position);
int type = getItemViewType(position);
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
if (type == SEPERATOR) {
convertView = inflater.inflate(R.layout.blogs_row_day_separator_item_layout, null);
View separator = convertView.findViewById(R.id.blogs_separator);
separator.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// do nothing
}
});
holder.separator = (TextView) separator.findViewById(R.id.blogs_row_day_separator_text);
} else {
convertView = inflater.inflate(R.layout.blogs_row_layout, null);
}
holder.usericon = (ImageView) convertView.findViewById(R.id.blogs_row_user_icon);
holder.title = (TextView) convertView.findViewById(R.id.blogs_row_title);
holder.date = (TextView) convertView.findViewById(R.id.blogs_row_date);
holder.amount = (TextView) convertView.findViewById(R.id.blogs_row_author);
holder.author = (TextView) convertView.findViewById(R.id.blogs_row_author);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (holder.separator != null) {
holder.separator
.setText(DateUtility.createDate(blog.blog.getUnixtime() * 1000L, "EEEE, dd. MMMMM yyyy"));
}
holder.usericon.setImageBitmap(blog.icon);
holder.title.setText(createTitle(blog.blog.getTitle()));
holder.date.setText(DateUtility.createDate(blog.blog.getUnixtime() * 1000L, "'um' HH:mm'Uhr'"));
holder.amount.setText(createCommentsAmount(blog.blog.getComments()));
holder.author.setText(createAuthor(blog.blog.getAuthor()));
return convertView;
}
class ViewHolder {
TextView separator;
ImageView usericon;
TextView title;
TextView date;
TextView amount;
TextView author;
}
/**
* Check if the blog on the given position must be separated from the last blogs.
*
* @param position
* @return
*/
private boolean isSeparator(int position) {
boolean separator = false;
// check if the last blog was created on the same date as the current blog
if (DateUtility.getDay(
DateUtility.createCalendarFromUnixtime(blogs.get(position - 1).getUnixtime() * 1000L), 0)
.getTimeInMillis() > blogs.get(position).getUnixtime() * 1000L) {
// current blog was not created on the same date as the last blog --> separator necessary
separator = true;
}
return separator;
}
}
private class UIBlog {
Blog blog;
CharSequence seperatorTxt;
Bitmap icon;
CharSequence titleTxt;
CharSequence dateTxt;
CharSequence amountTxt;
CharSequence authorTxt;
}