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

Jef*_*man 5 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();
}

}
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

Jos*_*arl 12

解决方案是实现更智能的何时获取缩略图的方法 - 当用户在列表中投掷时,它是无意义的获取缩略图.基本上你想要在Romain Guy的Shelves应用程序中实现的东西.

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

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

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

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

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


Dor*_*ori 12

我有一个答案给你!

当在内部setImage...调用任何方法时ImageView,请求布局传递,例如,setImageBitmap()如上所述定义

public void setImageBitmap(Bitmap bm) {
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}
Run Code Online (Sandbox Code Playgroud)

哪个叫

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

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

我为防止这种情况所做的是将View加载到Gallery中具有明确的高度和宽度(以dips为单位)并使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

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