这个主题已经并且仍然存在很多争论,我已经阅读了许多教程,提示并看到了有关它的讨论。但是当我达到某些行的复杂性时,我仍然遇到了为ListView实现自定义BaseAdapter的问题。 所以我基本上拥有的是通过解析来自网络的xml获得的一些实体。另外我获取一些图像等等,所有这些都是在AsyncTask中完成的。 我在getView()方法中使用性能优化的ViewHandler方法,并按照每个人的建议重用convertView。即我希望我正在使用ListView,因为它只是显示一个ImageView和两个TextViews,它们使用SpannableStringBuilder(我不使用任何HTML.fromHTML)。
现在它来了。每当我使用多个小型ImageView,一个Button以及一些使用SpannableStringBuilder进行不同样式设置的TextView扩展我的行布局时,我都会获得停止滚动的性能。该行包含一个RelativeLayout作为父级,所有其他元素都使用布局参数排列,因此我的布局不能使该行更简单。我必须承认,我从未见过任何包含许多UI元素的行的ListView实现示例。
但是,当我在ScrollView中使用TableLayout并手动填充AsyncTask(onProgressUpdate()稳定添加的新行)时,即使有数百行,它也表现得非常流畅。如果滚动到列表末尾,添加新行时会稍微绊倒一下。否则它比ListView更平滑,它在滚动时总是绊倒。
当ListView不想表现好时,有什么建议吗?我应该继续使用TableLayout方法还是建议使用ListView来优化性能?
以下是我的适配器的实现:
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(没有按钮,仍然是绊脚石):
<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>
的 的 ** * ** * ** * 的* 更新 * ** * ** * ** * ** < EM> *
事实证明,问题只是通过使用ArrayAdapter而不是BaseAdapter来解决。我使用与ArrayAdapter完全相同的代码,性能差异是GIGANTIC!它的运行方式与TableLayout一样流畅。
因此,每当我使用ListView时,我肯定会避免使用BaseAdapter,因为它对于复杂的布局而言要慢得多且不太优化。这是一个相当有趣的结论,因为我在示例和教程中没有读过关于它的单词。或者也许我没有准确地阅读它。 ; - )
然而,这是正常运行的代码(因为您可以看到我的解决方案是使用分隔符对列表进行分组):
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;
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STRONG> 只是为了表明BaseAdapter与ArrayAdapter不同。这只是来自getView()方法的整个跟踪,在两个适配器中使用完全相同的代码。
首先是调用量http://img845.imageshack.us/img845/5463/tracearrayadaptercalls.png
http://img847.imageshack.us/img847/7955/tracebaseadaptercalls.png
独家消费时间 http://img823.imageshack.us/img823/6541/tracearrayadapterexclus.png
http://img695.imageshack.us/img695/3613/tracebaseadapterexclusi.png
包容时间消耗 http://img13.imageshack.us/img13/4403/tracearrayadapterinclus.png
http://img831.imageshack.us/img831/1383/tracebaseadapterinclusi.png
正如您所看到的,这两个适配器之间存在巨大的差异(ArrayAdapter在getView()方法中快了四倍)。我真的不知道为什么这么戏剧化。我只能假设ArrayAdapter有一些更好的缓存或进一步优化。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 为了向您展示我当前的UIBlog类是如何构建的:
private class UIBlog {
Blog blog;
CharSequence seperatorTxt;
Bitmap icon;
CharSequence titleTxt;
CharSequence dateTxt;
CharSequence amountTxt;
CharSequence authorTxt;
}
为了说清楚,我正在将它用于BOTH适配器。
答案 0 :(得分:7)
您应该使用DDMS的探查器来确切地查看花费的时间。我怀疑你在getView()里面做的事情很昂贵。例如,viewUtility.setUserIcon(holder.usericon,blogs.get(position).getUid(),30);每次都创建一个新图标?一直解码图像会造成打嗝。
答案 1 :(得分:2)
阅读很多;)
我的布局中没有看到任何错误。 你可以优化 - 你的第一个如果有|| - 在一个变量中缓存blogs.get(position) - déclare你一直静止。 - 你为什么要使用日历并将它装扮回ms?你好像已经有了你的ms?
但我担心这还不够。
此致 斯特凡