我想创建一个延迟加载适配器,用于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
中慢慢滚动时,中心图像会不断弹出中心。确切地说,很难解释,但它真的很烦人。我也为旋转器适配器尝试了相同的方法,它在那里工作得很好。
有什么想法吗?
答案 0 :(得分:12)
解决方案是实现一种更智能的何时获取缩略图的方法 - 当用户浏览列表时,获取缩略图是毫无意义的。基本上你想要在Romain Guy的Shelves应用程序中实现类似的东西。
要获得响应最快的图库,您需要实现某种形式的内存缓存并执行以下操作:
getView
的内存缓存中存在图像时设置。设置一个标志,指示图像是否已设置或是否需要下载。您还可以在SD卡和内部存储器的高速缓存中维护内存,如果当前没有正在进行闪存,则显示低res(inSampleSize
设置为16或8)版本,仅在滚动时可见通过 - 高分辨率版本将在用户放手并加载图像时加载。OnItemSelectedListener
(并确保在初始化时调用setCallbackDuringFling(false)
),只有在用户手指启动时才下载需要下载的所有可见项目的新缩略图(您可以使用getFirstVisiblePosition
和getLastVisiblePosition
查找可见的视图范围)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以获取可能的解决方法。