我有一个ListView
,每个元素有一个图像和两行文本(由RelativeLayout
组织)。它工作正常,但它太慢了,我知道问题的来源!
这是我正在使用的自定义适配器的getView()
方法:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_view_item, parent, false);
mViewHolder = new ViewHolder();
mViewHolder.cover = (ImageView) convertView.findViewById(R.id.app_icon);
mViewHolder.title = (TextView) convertView.findViewById(R.id.selection);
mViewHolder.description = (TextView) convertView.findViewById(R.id.app_short_description);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) convertView.getTag();
}
// Here is the origin of the issue !
final Feed currentFeed = getItem(position);
mViewHolder.title.setText(currentFeed.getTitle());
mViewHolder.description.setText(currentFeed.getDescription());
try {
if(currentFeed.getThumbnailUrl() != null) {
downloadThumbnail(mViewHolder.cover, currentFeed.getThumbnailUrl());
}
} catch(Exception e) {
e.printStackTrace();
}
return convertView;
}
private static class ViewHolder {
TextView title;
TextView description;
ImageView cover;
}
所以我做了一些手动基准测试,似乎分配Feed
的实例是这种缓慢的根源:
final Feed currentFeed = getItem(position);
我知道这是因为我写了另一个版本来比较两者:
// Here is the origin of the issue !
//final Feed currentFeed = getItem(position);
mViewHolder.title.setText("Title");
mViewHolder.description.setText("Description");
try {
if(currentFeed.getThumbnailUrl() != null) {
downloadThumbnail(mViewHolder.cover, "some url");
}
} catch(Exception e) {
e.printStackTrace();
}
这个方法更顺畅(即使downloadThumbnail()
方法有效)。
我也确切地知道我的ListView
只有15个项目。
我知道因为垃圾收集而分配对象非常昂贵,但我无法用其他任何方式来做它!
有什么想法吗?
谢谢!
修改
不要过多关注downloadThumbnail()方法,它已经做了一些缓存。实际上,即使没有任何图片,它仍然很慢。
答案 0 :(得分:4)
当用户滚动列表时,将在适配器上调用getView。确保您不要重复执行相同的操作,例如生成缩略图。如果项目数量有限(例如视频内容),那么您可以创建所有视图并使其准备好进行查看。否则,您可能必须实现缓存。
下面的代码显示了一个适配器和listView实现,其中所有listviews都创建并存储在内存中。由于这是用于视频浏览,因此内存不会造成任何问题。 (内容数量有限,最多100个)
视频列表适配器
import java.util.ArrayList;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.LinearLayout.LayoutParams;
public class VideoListAdapter extends BaseAdapter {
private Context mContext = null;
private HashMap<String, VideoListItem> mHashedItems = new HashMap<String, VideoListItem>();
private static final String TAG = "VideoListAdapter";
public static final int VIDEO_CONTENT_ID = 0;
public static final int VIDEO_CONTENT_TITLE = 1;
public static final int VIDEO_CONTENT_DURATION = 2;
public static final int VIDEO_CONTENT_RESOLUTION = 3;
public static final int VIDEO_CONTENT_MIME = 4;
private Cursor mCursorForVideoList = null;
private ContentResolver mContentResolver = null;
private int mListCount = 0;
VideoListAdapter(Context context, ContentResolver cr) {
mContext = context;
mContentResolver = cr;
Log.i(TAG, "In the Constructor");
mCursorForVideoList =
mContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.TITLE,
MediaStore.Video.VideoColumns.DURATION,
MediaStore.Video.VideoColumns.RESOLUTION
},
null,
null,
null);
mListCount = mCursorForVideoList.getCount();
}
@Override
public int getCount() {
return mListCount;
}
@Override
public Object getItem(int arg0) {
return getVideoListItem(arg0);
}
@Override
public long getItemId(int position) {
//Log.i(TAG, "position : " + position);
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Log.i(TAG, "GetView :: Position : " + position);
return getVideoListItem(position);
}
private VideoListItem getVideoListItem(int position)
{
//Log.i(TAG, "getVideoListItem :: Position : " + position);
String key = Integer.toString(position);
VideoListItem item = mHashedItems.get(key);
if(item == null)
{
//Log.i(TAG, "New getVideoListItem :: Position : " + position);
mCursorForVideoList.moveToPosition(position);
mHashedItems.put(key, new VideoListItem(mContext, mContentResolver, mCursorForVideoList));
}
return mHashedItems.get(key);
}
};
视频列表视图
import java.util.Formatter;
import java.util.Locale;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.LinearLayout.LayoutParams;
class VideoListItem extends LinearLayout
{
private static final String TAG = "VideoListAdapter";
private ImageView mThumbnail = null;
private TextView mDuration = null;
private TextView mTitle = null;
private TextView mResolution = null;
private LayoutInflater mLayoutFactory = null;
private long mContentId = 0;
public VideoListItem(Context context, ContentResolver cr, Cursor cursor) {
super(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
params.setMargins(10, 10, 10, 10);
mLayoutFactory = LayoutInflater.from(context);
View thisView = mLayoutFactory.inflate(R.layout.videolistitem, null);
addView(thisView);
mThumbnail = (ImageView) findViewById(R.id.thumbnail);
mDuration = (TextView) findViewById(R.id.DDuration);
mTitle = (TextView) findViewById(R.id.DTitle);
mResolution = (TextView) findViewById(R.id.DResolution);
mThumbnail.setLayoutParams(new LinearLayout.LayoutParams(144, 144));
Resources r = this.getResources();
Bitmap bMap = MediaStore.Video.Thumbnails.getThumbnail(cr, cursor.getLong(VideoListAdapter.VIDEO_CONTENT_ID), MediaStore.Video.Thumbnails.MINI_KIND, null);
if(bMap != null)
{
mThumbnail.setImageBitmap(Bitmap.createScaledBitmap(bMap, 128, 128, true));
}
else
{
mThumbnail.setImageDrawable(r.getDrawable(R.drawable.error));
}
mThumbnail.setPadding(16, 16, 16, 16);
mTitle.setText(cursor.getString(VideoListAdapter.VIDEO_CONTENT_TITLE));
mTitle.setSingleLine();
mTitle.setTextColor(Color.GREEN);
mResolution.setText(cursor.getString(VideoListAdapter.VIDEO_CONTENT_RESOLUTION));
mResolution.setSingleLine();
mResolution.setTextColor(Color.RED);
mDuration.setText(stringForTime(cursor.getInt(VideoListAdapter.VIDEO_CONTENT_DURATION)));
mDuration.setSingleLine();
mDuration.setTextColor(Color.CYAN);
mContentId = cursor.getLong(VideoListAdapter.VIDEO_CONTENT_ID);
}
public long getContentId()
{
return mContentId;
}
private StringBuilder mFormatBuilder = null;
private Formatter mFormatter = null;
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
};
词shash
答案 1 :(得分:1)
不要在Feed支架中分配或存储Feed对象,而只存储位置(位置)。当您需要引用该对象时,请从ViewHolder中获取引用索引并相应地执行操作。
修改强>
当然我错过了你以后使用的对象...你也可以为你的Feed对象创建一些最小的静态方法,只返回特定的东西,比如标题等。然后在你的方法中调用这些方法。 getView方法设置UI元素而不完全创建Feed本身。