我正在使用延迟加载Gallery
个图片(一个类用于Gallery
和Adapter
,另一个用于延迟加载部分。第二个类使用runOnUiThread
使用其上下文更新第一个类,但似乎它再次调用第一个类'适配器的getView()
方法。这意味着,对于图库中的每个图像,都会调用getView()
两次。看看下面的代码。
奇怪的是,仅在getView()
小部件中的所选图像上调用第二个Gallery
调用。如果同时显示四个图像,则会在这四个图像上调用getView()
一次,并在所选图像上再调用三次。
为什么会发生这种情况?
这是适配器
public class ImageAdapter extends BaseAdapter {
public HorizontalImageLoader horImageLoader;
public ImageAdapter() {
horImageLoader = new HorizontalImageLoader(Main.this);
}
public int getCount() {
return coverFileNames.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(Main.this);
} else {
imageView = (ImageView) convertView;
}
// If I just use this, getView() is only called once per image (the correct way):
// imageView.setImageResource(R.drawable.noposterxl);
// If I just use this, getView() is only called once per
// image, and additional times on the selected image (not correct):
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);
return imageView;
}
}
这是HorizontalImageLoader类(based on this example)
public class HorizontalImageLoader {
private Activity activity;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public HorizontalImageLoader(Activity activity) {
this.activity = activity;
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
}
public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
imageViews.put(imageView, fileUrl);
queuePhoto(fileUrl, activity, imageView, pos);
imageView.setImageResource(R.drawable.noposterxl);
}
private void queuePhoto(String url, Activity activity, ImageView imageView, int position) {
//This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
photosQueue.Clean(imageView);
PhotoToLoad p=new PhotoToLoad(url, imageView, position);
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.push(p);
photosQueue.photosToLoad.notifyAll();
}
//start thread if it's not started yet
if(photoLoaderThread.getState()==Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(String fileUrl, int position) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bm = BitmapFactory.decodeFile(fileUrl, options);
return bm;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public int pos;
public PhotoToLoad(String u, ImageView i, int p){
url=u;
imageView=i;
pos = p;
}
}
PhotosQueue photosQueue=new PhotosQueue();
public void stopThread()
{
photoLoaderThread.interrupt();
}
//stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void Clean(ImageView image)
{
try {
for(int j=0 ;j<photosToLoad.size();){
if(photosToLoad.get(j).imageView==image)
photosToLoad.remove(j);
else
++j;
}
} catch (Exception e) {
// Do nothing
}
}
}
class PhotosLoader extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoad.size()==0)
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.wait();
}
if(photosQueue.photosToLoad.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoad){
photoToLoad=photosQueue.photosToLoad.pop();
}
Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.pos);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.equals(photoToLoad.url)){
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd); // This seems to be causing the additional calls to getView()
}
}
if(Thread.interrupted())
break;
}
} catch (InterruptedException e) {
//allow thread to exit
}
}
}
PhotosLoader photoLoaderThread=new PhotosLoader();
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i){
bitmap=b;
imageView=i;
}
public void run()
{
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(R.drawable.noposterxl);
}
}
}
再次更新
如果我在getView()
方法中执行以下操作,这就是LogCat所说的:
代码:
Log.d("POSITION", "Current position: " + position);
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);
logcat的:
09-03 13:59:11.920: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 2
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 3
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 0
09-03 13:59:12.110: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.240: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.300: DEBUG/POSITION(15278): Current position: 1
(注意底部有“当前位置:1”的其他三个日志条目)
如果我这样做,那么这就是LogCat所说的:
代码:
Log.d("POSITION", "Current position: " + position);
imageView.setImageResource(R.drawable.noposterxl);
logcat的:
09-03 14:02:47.340: DEBUG/POSITION(15412): Current position: 1
09-03 14:02:47.360: DEBUG/POSITION(15412): Current position: 2
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 3
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 0
(请注意,这会返回正确的结果 - 每张图片只能调用一次)
PS。我的Gallery
设置为首先选择索引1,这就是首先调用位置1的原因。
答案 0 :(得分:2)
执行:
public View getView(int position, View convertView, ViewGroup parent)
DisplayImage(coverFileNames.get(position), imageView, position)
请记住,如果getView
的执行中包含DisplayImage
,您将拥有无限循环。
由于没有无限循环,因此两种情况中的一种可能是真的:
DisplayImage
仅 ONCE 执行 > getView
。 2 不成立,因为getView
中执行的唯一语句是getView
1 为真,DisplayImage
执行DisplayImage
一种风格。
根据the source code of setImageDrawable
setImageDrawable
和View.requestLayout()
被调用。
View.invalidate
会根据文档调用View.requestLayout()
界面ViewParent
:
当某些内容发生更改时调用,这会使此视图父级的子级布局无效。这将安排视图树的布局传递。
requestLayout()
会调用View.invalidate
接口的ViewParent
,这可能会导致ViewParent本身失效。
因此,可以肯定地说,设置
invalidateChild
的图像会导致自身失效。 (事实确实如此,无论如何都不需要证明)
现在,由于此ImageView
是适配器接口的子级,因此调用适配器的ImageView
并使用包含此getView
<的精确视图的位置是最先进的。 / p>
最后, 担心不 ,因为在这种情况下 ImageView
与getView
同等,您肯定希望{{ 1}}被调用以便您的图像显示 。
这是正常的。
1更多内容
您的应用程序性能不佳不是因为调用了invalidate()
!
您应该正确实施invalidate()
。您的班级没有考虑任何优化。这不是getView
的责任。
的建议强> :
DisplayImage
函数总是调用BaseAdapter
函数!你一直在打扫卫生!queuePhoto
!并且你在循环中一直比较Clean
尝试增强代码并优化代码。您应该考虑多次调用getView。
答案 1 :(得分:2)
BitmapDisplayer是一个在UI线程上运行的Runnable,它调用ImageView上的方法。这些方法碰巧生成布局请求,最终会导致调用getView()。
答案 2 :(得分:1)
我可以看到代码中没有问题。我认为适配器视图在选择项目时调用getview是正常的。以下是另一个类似问题的链接,可以进一步解释。
custom listview adapter getView method being called multiple times, and in no coherent order
在setImageResource
中注释掉DisplayImage
行,看看针对所选项目的getView调用次数是否减少到2次。
public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
imageViews.put(imageView, fileUrl);
queuePhoto(fileUrl, activity, imageView, pos);
// imageView.setImageResource(R.drawable.noposterxl); // coment this line
}
答案 3 :(得分:1)
这不是奇怪的问题,在显示活动之前多次调用getView
方法是正常的,这都是关于验证视图的。
测试它尝试用文本创建普通列表,你会看到每个视图被调用两次。
答案 4 :(得分:0)
我认为这是listview高度的问题。如果您为listview高度提供一些固定高度而不是wrap_content
或fill_parent
,那么我认为它会起作用。检查一下。