在Android(初学者级别)Listview中的懒加载图像?
可能重复:
Android - How do I do a lazy load of images in ListView在Android(初学者级别)Listview中的懒加载图像?
我的工作与自定义适配器列表视图。我想加载它的图像和文本视图。这些图像是从互联网网址加载的。我只是想显示可见列表项的图像来激发用户。我提到了Shelves opensource project example from romainguy,但它很难理解代码。对于初学者级别,我只想知道如何处理适配器和活动之间的标签。从commonswareexample我可以在适配器上设置标签,但不能在列表视图的空闲状态下显示相应的图像。请帮助我提出你的想法。示例代码更容易理解。
谢谢。
编辑:
Efficient和Slow适配器在ApiDemos的组合是更有助于理解。
变化对有效的适配器例如做过这样的:
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,以便其他人可以使用它。
这里的基本讨论: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/
而且还有一类我记录后,它使用一个线程,并回调加载图像:
更新:为了解决您的特定的例外情况,我认为从列表getChildAt
返回的视图不是ImageView
- 它是用于保存图像和文本的任何布局视图。
更新包括相关代码:(每@乔治收纳器的建议)
下面是我用的适配器:
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;
}
}
据我所见,静态ViewHolder
没有任何帮助。尝试将整个onScrollStateChanged
函数放在/*
和*/
之间,删除static ViewHolder
行,并将holder = new ViewHolder();
更改为ViewHolder holder = new ViewHolder();
。
“试着将整个onScrollStateChanged函数放在/ *和* /”之间。它没有任何意义。那么我们如何跟踪滚动状态以加载可见的特定行。 – Praveen 2010-05-27 08:10:20
它们在适配器的getView函数中加载。 – 2010-05-27 20:42:22
啊,请检查您的logcat,以确保您的应用程序ISN没有被杀害和重新启动。大多数手机将您的总应用程序大小限制为16mb或24mb。很容易加载一堆图像,运行,杀死,重新启动,并让onPause不在屏幕上加载大数据。这是穷人的garabage集合。
我编辑了我的问题。请看看它。 – Praveen 2010-05-31 08:22:27
据我所知,您需要在滚动完成后更新您的列表。这很容易。下面是固定的代码为您提供:
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会告诉适配器重新显示所有可见的物品,所以他们会与图像2中显示。
我明白了。这是我想要的完美代码。延迟加载工作到自定义适配器只是可见列表项的图标。希望对初学者有所帮助
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 ...你可以简化,如果你避免持有人的事情或更快,如果你在持有人的帮助下设置的东西... ... – Karussell 2011-04-08 10:07:17
你知道如何获得图像视图对象吗?我看到并使用了你的代码。但是,没有正确加载图像。一个或两个图像没有完成丢失。并获得Outofmemory异常。为什么?愿你呢? – Praveen 2010-05-28 08:46:40
您需要跟踪您的视图层次结构。在ListView上调用getChildAt(index)将返回可见列表顶部索引的列表视图项(布局视图)。在该视图对象上,您可以调用findViewById来获取图像视图。 – jwadsack 2010-05-28 19:42:26
请从您的博客相关部分到这里。没有这一点,当这些链接死亡时,你的答案的有效性就会随之消失。 – 2012-07-02 23:55:13