android画廊视图“口吃”与延迟图像加载适配器

时间:2011-04-22 17:59:49

标签: android android-gallery android-adapter

我想创建一个延迟加载适配器,用于Gallery小部件。

也就是说getView()立即返回ImageView,稍后其他一些机制将异步调用其setImageBitmap()方法。我通过创建一个扩展ImageView的“懒惰”ImageView来完成此操作。

public class GalleryImageView extends ImageView {

    // ... other stuff here ...

    public void setImage(final Looper looper, final int position) {

    final Uri uri = looper.get(position);
    final String path = looper.sharePath(position);

    new Thread(new Runnable() {

        @Override
        public void run() {
            GalleryBitmap gbmp = new GalleryBitmap(context, uri, path);
            final Bitmap bmp = gbmp.getBitmap(); // all the work is here
            handler.post(new Runnable() {

                @Override
                public void run() {
                    if (GalleryImageView.this.getTag().equals(uri)) {
                        setImageBitmap(bmp);
                    }
                }
            });
        }
    }).start();
}

}

当我在Gallery中慢慢滚动时,中心图像会不断弹出中心。确切地说,很难解释,但它真的很烦人。我也为旋转器适配器尝试了相同的方法,它在那里工作得很好。

有什么想法吗?

3 个答案:

答案 0 :(得分:12)

解决方案是实现一种更智能的何时获取缩略图的方法 - 当用户浏览列表时,获取缩略图是毫无意义的。基本上你想要在Romain Guy的Shelves应用程序中实现类似的东西。

要获得响应最快的图库,您需要实现某种形式的内存缓存并执行以下操作:

  • 仅在您getView的内存缓存中存在图像时设置。设置一个标志,指示图像是否已设置或是否需要下载。您还可以在SD卡和内部存储器的高速缓存中维护内存,如果当前没有正在进行闪存,则显示低res(inSampleSize设置为16或8)版本,仅在滚动时可见通过 - 高分辨率版本将在用户放手并加载图像时加载。
  • 添加OnItemSelectedListener(并确保在初始化时调用setCallbackDuringFling(false)),只有在用户手指启动时才下载需要下载的所有可见项目的新缩略图(您可以使用getFirstVisiblePositiongetLastVisiblePosition查找可见的视图范围)
  • 此外,当用户抬起他们的手指检查以查看1.如果所选位置因用户放下手指而改变,如果是这样2.是否由于您的OnItemSelectedListener而启动了下载 - 如果不是然后启动一个。这是为了捕捉不发生投掷的情况,因此OnItemSelected从不做任何事情,因为在这种情况下总是用手指调用它。我会使用Handler来延迟按照您的图库的动画时间开始下载(确保在调用onItemSelected时或当您收到ACTION_DOWN事件时清除发布到此处理程序的所有延迟消息。
  • 下载图像后,检查是否有任何可见视图请求此图像,然后更新这些视图

另请注意,默认的Gallery组件未正确实现View回收(它假定适配器中的每个位置都有一个独特的视图,并且当它们离开屏幕时也会清除这些项目的回收者,使其变得毫无意义)。 编辑:更多看起来并非毫无意义 - 但就下一个/上一个观点而言,它不是回收者,而是避免在布局期间为当前视图调用getView变化。

这意味着传递给convertView方法的getView参数通常不会为空,这意味着您将夸大很多视图(这很昂贵) - 请参阅我对{的回答{3}}提供一些提示。 (PS:我已经修改了代码 - 我会在布局阶段使用不同的回收站进行布局阶段和滚动阶段,并根据布局回收站中的位置检索视图,如果是,请不要调用getView你从bin获取的视图是非null,因为它将是完全相同的视图;在布局阶段之后也清除布局回收站 - 这使得事情变得更加快捷一些)

PS:同样要小心你在OnItemSelected中所做的事情 - 即除非它在上面提到的地方然后尝试做尽可能少的事情。例如,我在TextView的我的图库上方OnItemSelected设置了一些文字。只需将此调用移动到与更新缩略图的位置相同的点,就会产生明显的差异。

答案 1 :(得分:12)

我有一个答案!

如果在setImage...内部调用任何ImageView方法,则会请求布局传递,例如,setImageBitmap()如上所述

public void setImageBitmap(Bitmap bm) {
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}

调用

public void setImageDrawable(Drawable drawable) {
    if (mDrawable != drawable) {
        mResource = 0;
        mUri = null;
        updateDrawable(drawable);
        requestLayout(); //layout requested here!
        invalidate();
    }
}

具有画廊'捕捉'到当前最接近画廊中心的图像中心的效果。

我为防止这种情况所做的是将View加载到Gallery中具有明确的高度和宽度(在dip s中)并使用忽略布局请求的ImageView子类。这是有效的,因为画廊最初仍然有一个布局通道,但每次画廊中的图像更改时都不会这样做,我想只有在画廊视图的宽度和高度设置为WRAP_CONTENT时才需要这样做,我们不。请注意,由于invalidate()仍在setImageDrawable()中调用,因此设置时仍会绘制图像。

下面我非常简单的ImageView子类!

/**
 * This class is useful when loading images (say via a url or file cache) into
 * ImageView that are contained in dynamic views (Gallerys and ListViews for
 * example) The width and height should be set explicitly instead of using
 * wrap_content as any wrapping of content will not be triggered by the image
 * drawable or bitmap being set (which is normal behaviour for an ImageView)
 * 
 */
public class ImageViewNoLayoutRefresh extends ImageView
{
    public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public ImageViewNoLayoutRefresh(Context context)
    {
        super(context);
    }

    @Override
    public void requestLayout()
    {
        // do nothing - for this to work well this image view should have its dims
        // set explicitly
    }
}

编辑:我应该提一下onItemSelected方法也可以工作,但是因为我需要在进行投掷的同时进行,我想出了上面的内容,我认为这是更灵活的方法

答案 2 :(得分:2)

这可能是Gallery的onLayout方法中的错误。查看http://code.google.com/p/android/issues/detail?id=16171以获取可能的解决方法。