在android(初级)的Listview上延迟加载图像? [复制]
Posted
技术标签:
【中文标题】在android(初级)的Listview上延迟加载图像? [复制]【英文标题】:Lazy Load images on Listview in android(Beginner Level)? [duplicate] 【发布时间】:2011-02-24 02:51:23 【问题描述】:可能重复:android - How do I do a lazy load of images in ListView
我正在使用自定义适配器处理列表视图。我想在上面加载图像和文本视图。图片是从互联网网址加载的。我只想向 hte 用户显示可见列表项的图像。我参考了Shelves opensource project example from romainguy,但理解代码很复杂。对于初学者来说,我只想知道如何处理适配器和活动之间的标签。从commonsware example 我可以在适配器上设置标签,但在列表视图的空闲状态下无法显示相应的图像。请帮我谈谈你的想法。示例代码更容易理解。
谢谢。
编辑:
ApiDemos中Efficient和SlowAdapter的组合更有助于理解。
对高效适配器示例所做的更改如下:
public class List14 extends ListActivity implements ListView.OnScrollListener
// private TextView mStatus;
private static boolean mBusy = false;
static ViewHolder holder;
public static class EfficientAdapter extends BaseAdapter
private LayoutInflater mInflater;
private Bitmap mIcon1;
private Bitmap mIcon2;
public EfficientAdapter(Context context)
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
// Icons bound to the rows.
mIcon1 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_2);
/**
* The number of items in the list is determined by the number of
* speeches in our array.
*
* @see android.widget.ListAdapter#getCount()
*/
public int getCount()
return DATA.length;
/**
* Since the data comes from an array, just returning the index is
* sufficent to get at the data. If we were using a more complex data
* structure, we would return whatever object represents one row in the
* list.
*
* @see android.widget.ListAdapter#getItem(int)
*/
public Object getItem(int position)
return position;
/**
* Use the array index as a unique id.
*
* @see android.widget.ListAdapter#getItemId(int)
*/
public long getItemId(int position)
return position;
/**
* Make a view to hold each row.
*
* @see android.widget.ListAdapter#getView(int, android.view.View,
* android.view.ViewGroup)
*/
public View getView(int position, View convertView, ViewGroup parent)
// A ViewHolder keeps references to children views to avoid
// unneccessary calls
// to findViewById() on each row.
// When convertView is not null, we can reuse it directly, there is
// no need
// to reinflate it. We only inflate a new View when the convertView
// supplied
// by ListView is null.
if (convertView == null)
convertView = mInflater.inflate(R.layout.list_item_icon_text,
null);
// Creates a ViewHolder and store references to the two children
// views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
else
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
if (!mBusy)
holder.icon.setImageBitmap(mIcon1);
// Null tag means the view has the correct data
holder.icon.setTag(null);
else
holder.icon.setImageBitmap(mIcon2);
// Non-null tag means the view still needs to load it's data
holder.icon.setTag(this);
holder.text.setText(DATA[position]);
// Bind the data efficiently with the holder.
// holder.text.setText(DATA[position]);
return convertView;
static class ViewHolder
TextView text;
ImageView icon;
private Bitmap mIcon1;
private Bitmap mIcon2;
@Override
public void onCreate(Bundle savedInstanceState)
mIcon1 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_2);
super.onCreate(savedInstanceState);
setListAdapter(new EfficientAdapter(this));
getListView().setOnScrollListener(this);
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
public void onScrollStateChanged(AbsListView view, int scrollState)
switch (scrollState)
case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
int first = view.getFirstVisiblePosition();
int count = view.getChildCount();
for (int i = 0; i < count; i++)
holder.icon = (ImageView) view.getChildAt(i).findViewById(
R.id.icon);
if (holder.icon.getTag() != null)
holder.icon.setImageBitmap(mIcon1);
holder.icon.setTag(null);
// mStatus.setText("Idle");
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
// mStatus.setText("Touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
// mStatus.setText("Fling");
break;
private static final String[] DATA = "Abbaye de Belloc",
"Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu";
现在可以正常使用了。但是当滚动状态时,它没有正确重新加载图像。一些项目间隔不显示图像2。这是加载图像的正确顺序。但并非在列表的所有项目中。实体项目间隔之间发生不匹配。 如何纠正?
【问题讨论】:
【参考方案1】:我明白了。这是我想要的完美代码。延迟加载适用于自定义适配器,只是可见列表项的图标。希望对初学者有所帮助
public class List14 extends ListActivity implements ListView.OnScrollListener
// private TextView mStatus;
private static boolean mBusy = false;
static ViewHolder holder;
public static class EfficientAdapter extends BaseAdapter
private LayoutInflater mInflater;
private Bitmap mIcon1;
private Bitmap mIcon2;
private Context mContext;
public EfficientAdapter(Context context)
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mContext = context;
// Icons bound to the rows.
mIcon1 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_2);
/**
* The number of items in the list is determined by the number of
* speeches in our array.
*
* @see android.widget.ListAdapter#getCount()
*/
public int getCount()
return DATA.length;
/**
* Since the data comes from an array, just returning the index is
* sufficent to get at the data. If we were using a more complex data
* structure, we would return whatever object represents one row in the
* list.
*
* @see android.widget.ListAdapter#getItem(int)
*/
public Object getItem(int position)
return position;
/**
* Use the array index as a unique id.
*
* @see android.widget.ListAdapter#getItemId(int)
*/
public long getItemId(int position)
return position;
/**
* Make a view to hold each row.
*
* @see android.widget.ListAdapter#getView(int, android.view.View,
* android.view.ViewGroup)
*/
public View getView(int position, View convertView, ViewGroup parent)
// A ViewHolder keeps references to children views to avoid
// unneccessary calls
// to findViewById() on each row.
// When convertView is not null, we can reuse it directly, there is
// no need
// to reinflate it. We only inflate a new View when the convertView
// supplied
// by ListView is null.
if (convertView == null)
convertView = mInflater.inflate(R.layout.list_item_icon_text,
parent, false);
// Creates a ViewHolder and store references to the two children
// views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
else
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
if (!mBusy)
holder.icon.setImageBitmap(mIcon1);
// Null tag means the view has the correct data
holder.icon.setTag(null);
else
holder.icon.setImageBitmap(mIcon2);
// Non-null tag means the view still needs to load it's data
holder.icon.setTag(this);
holder.text.setText(DATA[position]);
// Bind the data efficiently with the holder.
// holder.text.setText(DATA[position]);
return convertView;
static class ViewHolder
TextView text;
ImageView icon;
private Bitmap mIcon1;
private Bitmap mIcon2;
@Override
public void onCreate(Bundle savedInstanceState)
mIcon1 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_2);
super.onCreate(savedInstanceState);
setListAdapter(new EfficientAdapter(this));
getListView().setOnScrollListener(this);
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
public void onScrollStateChanged(AbsListView view, int scrollState)
switch (scrollState)
case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
int first = view.getFirstVisiblePosition();
int count = view.getChildCount();
for (int i = 0; i < count; i++)
holder.icon = (ImageView) view.getChildAt(i).findViewById(
R.id.icon);
if (holder.icon.getTag() != null)
holder.icon.setImageBitmap(IMAGE[first+i]);// this is the image url array.
holder.icon.setTag(null);
// mStatus.setText("Idle");
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
// mStatus.setText("Touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
// mStatus.setText("Fling");
break;
private static final String[] DATA = "Abbaye de Belloc",
"Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
"Yarra Valley Pyramid", "Yorkshire Blue", "Zamorano",
"Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" ;
【讨论】:
你使用一个持有人然后总是使用 findViewById 有点奇怪......如果你避免那个持有人的东西,或者如果你在持有人的帮助下设置东西,你可以让它变得简单...【参考方案2】:普雷文-
正如您已经找到我的博客文章一样,我只是想将它推回 ***,以便其他人可以使用它。
以下是基本讨论: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/
还有一个我稍后记录的类,它使用线程和回调来加载图像:
http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/
更新:为了解决您的特定异常,我认为从getChildAt
返回的列表中的视图不是ImageView
- 它是您用来保存图像的任何布局视图,并且文本。
更新以包含相关代码:(根据@george-stocker 的建议)
这是我使用的适配器:
public class MediaItemAdapter extends ArrayAdapter<MediaItem>
private final static String TAG = "MediaItemAdapter";
private int resourceId = 0;
private LayoutInflater inflater;
private Context context;
private ImageThreadLoader imageLoader = new ImageThreadLoader();
public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems)
super(context, 0, mediaItems);
this.resourceId = resourceId;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.context = context;
@Override
public View getView(int position, View convertView, ViewGroup parent)
View view;
TextView textTitle;
TextView textTimer;
final ImageView image;
view = inflater.inflate(resourceId, parent, false);
try
textTitle = (TextView)view.findViewById(R.id.text);
image = (ImageView)view.findViewById(R.id.icon);
catch( ClassCastException e )
Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
throw e;
MediaItem item = getItem(position);
Bitmap cachedImage = null;
try
cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener()
public void imageLoaded(Bitmap imageBitmap)
image.setImageBitmap(imageBitmap);
notifyDataSetChanged();
);
catch (MalformedURLException e)
Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e);
textTitle.setText(item.name);
if( cachedImage != null )
image.setImageBitmap(cachedImage);
return view;
【讨论】:
【参考方案3】:据我了解,您需要在滚动完成后更新您的列表。这很简单。这是给你的固定代码:
EfficientAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState)
mIcon1 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(this.getResources(),
R.drawable.icon48x48_2);
super.onCreate(savedInstanceState);
adapter=new EfficientAdapter(this);
setListAdapter(adapter);
getListView().setOnScrollListener(this);
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
public void onScrollStateChanged(AbsListView view, int scrollState)
switch (scrollState)
case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
adapter.notifyDataSetChanged()
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
// mStatus.setText("Touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
// mStatus.setText("Fling");
break;
notifyDataSetChanged 将告诉适配器重新显示所有可见项目,因此它们将与 image2 一起显示。
【讨论】:
【参考方案4】:据我所知,静态 ViewHolder
没有任何帮助。尝试将整个onScrollStateChanged
函数放在/*
和*/
之间,删除static ViewHolder
行,并将holder = new ViewHolder();
更改为ViewHolder holder = new ViewHolder();
。
【讨论】:
【参考方案5】:啊,检查您的 logcat 以确保您的应用没有被杀死并重新启动。大多数手机将您的总应用程序大小限制为 16mb 或 24mb。加载一堆图像、运行、被杀死、重新启动以及让您的 onPause 不会将大数据加载到屏幕外很容易。这是穷人的垃圾收集。
【讨论】:
以上是关于在android(初级)的Listview上延迟加载图像? [复制]的主要内容,如果未能解决你的问题,请参考以下文章