gpt4 book ai didi

Android:ListView性能还是不够怎么办?

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:33:53 27 4
gpt4 key购买 nike

好吧,这个话题过去和现在都争论不休,我已经阅读了许多教程、提示并看到了关于它的讨论。但是每当我的行达到一定的复杂性时,我在为 ListView 实现自定义 BaseAdapter 时仍然遇到问题。所以我基本上拥有的是一些通过解析来自网络的 xml 获得的实体。此外,我获取了一些图像等,所有这些都是在 AsyncTask 中完成的。我在我的 getView() 方法中使用了性能优化 ViewHandler 方法,并按照大家的建议重用了 convertView。 IE。我希望我按预期使用 ListView,当我只显示一个 ImageView 和两个 TextView 时它确实工作正常,它们使用 SpannableStringBuilder 设置样式(我不使用任何 HTML.fromHTML)。

现在它来了。每当我使用多个小 ImageView、一个 Button 和更多使用 SpannableStringBuilder 设置不同样式的 TextView 扩展我的行布局时,我都会停止滚动性能。该行由一个 RelativeLayout 作为父级组成,所有其他元素都使用布局参数排列,因此我无法让该行的布局更简单。我必须承认,我从未见过任何 ListView 实现示例,其中的行包含那么多 UI 元素。

但是,当我在 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 使用了完全相同的代码,性能差异是巨大的!它运行起来与 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;
}
}

++++++++++++++++++ 带痕迹的第二次编辑+++++++++++++++++++++++ 只是为了表明 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;
}

为了清楚起见,我将它用于两个适配器。

最佳答案

您应该使用 DDMS 的分析器来准确查看时间花在了哪里。我怀疑您在 getView() 中所做的事情很昂贵。例如,viewUtility.setUserIcon(holder.usericon, blogs.get(position).getUid(), 30);每次都创建一个新图标?一直解码图像会造成问题。

关于Android:ListView性能还是不够怎么办?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7247301/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com