android图库视图“延迟”与延迟图像加载适配器
我想创建一个延迟加载适配器与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
中缓慢滚动时,中心图像不断弹出到中心。真的很难解释,但它真的很烦人。我也尝试了相同的方法为一个微调适配器,它在那里完美的作品。
任何想法?
解决的办法是实现一个更加智能的提取缩略图的方法 - 当用户在列表中投掷时,获取缩略图毫无意义。基本上你想要的是类似Romain Guy的Shelves应用程序中实现的。
为了获得最敏感的画廊,你需要实现某种形式的内存缓存,并做到以下几点:
- 只有当它在内存中缓存中存在从
getView
设置图像。设置一个标志,指示是否设置了图像或是否需要下载。您还可以在SD卡和内存上的缓存中保留一个内存,如果当前没有进行冲突,则显示低分辨率(inSampleSize
设置为16或8)版本,在滚动时会显示 - 高当用户放开并安置在图像上时,res版本将会加载。 - 添加
OnItemSelectedListener
(并确保在初始化时调用setCallbackDuringFling(false)
),其下载所有需要下载可见项新的缩略图,只有当用户手指向上(可以使用getFirstVisiblePosition
和getLastVisiblePosition
找到的范围视图可见) - 此外,当用户提起手指时,请检查以查看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
中的一些文字。只需将此调用与我更新缩略图的相同点进行了区分即可发现。
啊,谢谢。这似乎会让我朝着一个合理的方向前进。虽然在这一点上,我可能会在工作项目上踢球,只是为了获得固定数量的静态图像并一起切换滚动。至少对于这个版本。 :) – 2011-05-04 14:27:06
所以它不会加载任何图像,直到用户选择在画廊中的东西?如果用户无法看到他们,他们如何知道他们想要选择什么?我肯定错过了什么。看起来好像你想在投掷动作停止时开始下载/载入图像任务,而不是在选择项目时。如何知道画廊完成滚动的时间?该应用程序采用的方法是首先加载缩减采样的图像,然后加载完整的res版本。如果问题是网络下载,这不起作用。 – 2011-05-04 18:16:46
在这种情况下,显示一个加载微调器,直到下载图像。此外,Gallery确实创建了不可见的视图,因此,下载“onItemSelected”中的缩略图将下载左侧和右侧几个视图的图像。如果你想告诉画廊完成滚动的时间 - 即当你的OnItemSelected监听器被调用并且没有手指停止时 - 每当一个项目移动到中心时,监听器就会被调用。 – 2011-05-04 18:28:22
这可能是图库的onLayout方法中的一个错误。查看http://code.google.com/p/android/issues/detail?id=16171可能的解决方法。
主要问题,虽然这是我不相信这个解决方案将工作相同的问题。虽然它会在滚动期间停止所有的布局请求(并在250毫秒之后),但它不会在投掷时停止布局请求(请参阅图库的onFling)。据我所知,由于所有相关的方法和字段都是私人的(在图库中),所以无法确定投掷时间结束的时间,所以我认为您可以做的最好(除了在此页面上查看我的回答外)是从传递给这个方法的速度的一些函数中解决的,但是看着FlingRunnable和Scroller类,这不是简单的:) – Dori 2011-07-21 08:33:04
我有一个答案给你!
当任何的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方法也可以工作,但我需要挂接到,虽然丢正在发生,我想出了上面的,我认为这是更灵活的方式
我有同样的问题。滚动是粘性的,整个事情都是左右两边的温差,然后再回到原来的位置。你有没有找到解决方案? – 2011-05-03 21:56:46
您正在使用哪个版本的Android?当您不使用自定义ImageView时会发生此问题吗? 所有图像加载后问题是否会停止? – 2011-05-04 04:48:03
您可以请详细描述什么是您的'画廊' – 2011-05-05 12:54:55