2011-04-22 104 views
5

我想创建一个延迟加载适配器与Gallery部件一起使用。android图库视图“延迟”与延迟图像加载适配器

也就是说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中缓慢滚动时,中心图像不断弹出到中心。真的很难解释,但它真的很烦人。我也尝试了相同的方法为一个微调适配器,它在那里完美的作品。

任何想法?

+0

我有同样的问题。滚动是粘性的,整个事情都是左右两边的温差,然后再回到原来的位置。你有没有找到解决方案? – 2011-05-03 21:56:46

+0

您正在使用哪个版本的Android?当您不使用自定义ImageView时会发生此问题吗? 所有图像加载后问题是否会停止? – 2011-05-04 04:48:03

+0

您可以请详细描述什么是您的'画廊' – 2011-05-05 12:54:55

回答

12

解决的办法是实现一个更加智能的提取缩略图的方法 - 当用户在列表中投掷时,获取缩略图毫无意义。基本上你想要的是类似Romain Guy的Shelves应用程序中实现的。

为了获得最敏感的画廊,你需要实现某种形式的内存缓存,并做到以下几点:

  • 只有当它在内存中缓存中存在从getView设置图像。设置一个标志,指示是否设置了图像或是否需要下载。您还可以在SD卡和内存上的缓存中保留一个内存,如果当前没有进行冲突,则显示低分辨率(inSampleSize设置为16或8)版本,在滚动时会显示 - 高当用户放开并安置在图像上时,res版本将会加载。
  • 添加OnItemSelectedListener(并确保在初始化时调用setCallbackDuringFling(false)),其下载所有需要下载可见项新的缩略图,只有当用户手指向上(可以使用getFirstVisiblePositiongetLastVisiblePosition找到的范围视图可见)
  • 此外,当用户提起手指时,请检查以查看1.自用户放下手指后选择的位置是否发生了变化,如果是的话2.是否由于您的OnItemSelectedListener而发起了下载 - 如果不是然后启动一个。这是为了捕捉不发生抛掷的情况,因此OnItemSelected从不做任何事情,因为在这种情况下总是用手指调用它。我会使用处理程序来延迟开始下载您的画廊的动画时间(确保清除任何延迟的消息张贴到此处理程序每​​当onItemSelected被称为或当您得到ACTION_DOWN事件
  • 图像下载后检查是否有任何可见的意见要求该图像,然后和更新这些观点

另外要注意的是,默认的库组件不正确地实现搜索回收(它假定在适配器的每个位置有独特的见解,且同时清零当他们离开屏幕时,这些物品的回收者使它变得毫无意义)编辑:看起来它并不是毫无意义的 - 但它不是毫无意义的回收者s的下一个/前一个视图,而不是在布局更改期间避免必须针对当前视图调用getView

这意味着传递给你的getView方法convertView参数将更多的时候是不能为空,这意味着你会被夸大了很多意见(这是昂贵的) - 见我的回答Does a replacement for Gallery with View recycling exist?对一些提示。 (PS:我已经修改了代码 - 我将在布局阶段使用不同的回收站进行布局阶段和滚动阶段,并根据布局回收站的位置检索布局回收站中的视图,并且不要调用getView查看你从bin中获得的非null值,因为它将完全相同的视图;在布局阶段之后也清除布局回收站 - 这使事情变得更加快捷)

PS:同时要非常小心你在做什么OnItemSelected - 即除非它在上面提到的地方,然后尽量少做。例如,我在OnItemSelected之上设置了我的画廊上方TextView中的一些文字。只需将此调用与我更新缩略图的相同点进行了区分即可发现。

+0

啊,谢谢。这似乎会让我朝着一个合理的方向前进。虽然在这一点上,我可能会在工作项目上踢球,只是为了获得固定数量的静态图像并一起切换滚动。至少对于这个版本。 :) – 2011-05-04 14:27:06

+0

所以它不会加载任何图像,直到用户选择在画廊中的东西?如果用户无法看到他们,他们如何知道他们想要选择什么?我肯定错过了什么。看起来好像你想在投掷动作停止时开始下载/载入图像任务,而不是在选择项目时。如何知道画廊完成滚动的时间?该应用程序采用的方法是首先加载缩减采样的图像,然后加载完整的res版本。如果问题是网络下载,这不起作用。 – 2011-05-04 18:16:46

+0

在这种情况下,显示一个加载微调器,直到下载图像。此外,Gallery确实创建了不可见的视图,因此,下载“onItemSelected”中的缩略图将下载左侧和右侧几个视图的图像。如果你想告诉画廊完成滚动的时间 - 即当你的OnItemSelected监听器被调用并且没有手指停止时 - 每当一个项目移动到中心时,监听器就会被调用。 – 2011-05-04 18:28:22

2

这可能是图库的onLayout方法中的一个错误。查看http://code.google.com/p/android/issues/detail?id=16171可能的解决方法。

+0

主要问题,虽然这是我不相信这个解决方案将工作相同的问题。虽然它会在滚动期间停止所有的布局请求(并在250毫秒之后),但它不会在投掷时停止布局请求(请参阅图库的onFling)。据我所知,由于所有相关的方法和字段都是私人的(在图库中),所以无法确定投掷时间结束的时间,所以我认为您可以做的最好(除了在此页面上查看我的回答外)是从传递给这个方法的速度的一些函数中解决的,但是看着FlingRunnable和Scroller类,这不是简单的:) – Dori 2011-07-21 08:33:04

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(); 
    } 
} 

其具有画廊'的影响'的图像中心目前最接近的画廊中心。

我已经做了什么来防止这种情况,加载到图库中的视图具有明确的高度和宽度(在dip中),并使用忽略布局请求的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方法也可以工作,但我需要挂接到,虽然丢正在发生,我想出了上面的,我认为这是更灵活的方式

+1

太棒了。我试图实现我自己的ImageView,它在设置drawable时不会调用requestLayout(),但可以简单地覆盖requestLayout并不执行任何操作。 Ty man – weakwire 2011-08-08 14:22:35

+1

很好的回答,谢谢。 – Ljdawson 2011-11-04 21:20:27

+0

这节省了我很多时间。谢啦 – 2014-12-03 05:02:54