android 画廊视图“口吃”带有延迟图像加载适配器
我想创建一个延迟加载适配器以与 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();
}
}
当我在图库
中缓慢滚动时,中心图像不断弹出到中心。确切地说,很难解释,但这确实很烦人。我还对旋转适配器尝试了相同的方法,并且它在那里完美地工作。
有什么想法吗?
I would like to create an deferred loading adapter for use with a Gallery
widget.
That is to say getView()
returns an ImageView
immediately, and later some other mechanism will asynchronously call its setImageBitmap()
method. I did this by creating a "lazy" ImageView
that extends 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();
}
}
When I scroll slowly in the Gallery
, the center image keeps popping into the center. It's hard to explain, exactly, but it's really annoying. I also tried the same approach for a spinner adapter and it works perfectly there.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
解决方案是实现一种更智能的方法来确定何时获取缩略图 - 当用户快速浏览列表时获取缩略图是毫无意义的。本质上,您希望在 Romain Guy 的 Shelves 应用程序中实现类似的功能。
要获得响应速度最快的图库,您需要实现某种形式的内存缓存并执行以下操作:
getView
中设置内存缓存中存在的图像。设置一个标志,指示图像是否已设置或是否需要下载。您还可以在 SD 卡和内部存储器上的缓存中维护内存,如果当前未进行滑动,则显示低分辨率(inSampleSize
设置为 16 或 8)版本,该版本将可见当只是滚动浏览时 - 当用户放开并选择图像时,将加载高分辨率版本。OnItemSelectedListener
(并确保在初始化时调用setCallbackDuringFling(false)
),仅当用户手指向上(您可以使用getFirstVisiblePosition
和getLastVisiblePosition
来查找可见视图的范围)OnItemSelectedListener
启动了下载 - 如果没有,则启动下载。这是为了捕获没有发生滑动的情况,因此OnItemSelected
永远不会执行任何操作,因为在这种情况下总是用手指向下调用它。我将使用处理程序来延迟开始下载画廊的动画时间(请确保在调用onItemSelected
或收到ACTION_DOWN 时清除发布到此处理程序的任何延迟消息 另
请注意,默认的 Gallery 组件无法正确实现视图回收(它假设适配器中的每个位置都有一个唯一的视图) ,并且还清除了这些的回收器编辑:在更多的情况下,它并不是毫无意义的 - 但它不是下一个/上一个视图的回收器,而是为了避免调用。布局更改期间的当前视图的
getView
这意味着传递给
getView
方法的convertView
参数通常不为 null,这意味着您。会夸大很多观点(这是昂贵的) - 请参阅我对 的回答是否存在具有 View 回收功能的 Gallery 替代品? 以获得有关这方面的一些提示。 (PS:我已经修改了该代码 - 我会在布局阶段和滚动阶段使用不同的回收站,并根据其位置检索布局回收站中的视图,并且如果从垃圾箱中获得的视图是非空的,因为它在布局阶段之后也会清除布局回收站——这使得事情变得更加快捷)PS:还要非常小心你所做的事情
OnItemSelected
- 即除非就在上面提到的地方,那就尽量少做。例如,我在OnItemSelected
中的 Gallery 上方的TextView
中设置一些文本。只需将此调用移至与我更新缩略图的位置相同的位置即可产生显着的差异。The solution is to implement a more intelligent method of when to fetch thumbnails - it is pointless fetching thumbnails while the user is flinging through the list. Essentially you want something like that implemented in Romain Guy's Shelves application.
To get the most responsive Gallery you'll need to implement some form of in-memory cache and do the following:
getView
. Set a flag indicating whether the image was set or whether a download is required. You could also maintain a memory in a cache on the SD card and internal memory, and if a fling is not currently ongoing then show a low res (inSampleSize
set to 16 or 8) version which will be visible when just scrolling through - the high res version will load when the user lets go and settles on an image.OnItemSelectedListener
(and make sure to callsetCallbackDuringFling(false)
when initializing) that downloads new thumbnails for all the visible items that require a download only if the users finger is up (you can usegetFirstVisiblePosition
andgetLastVisiblePosition
to find the range of views visible)OnItemSelectedListener
- if it wasn't then initiate one. This is to catch the case where no flinging occurs, and thusOnItemSelected
never does anything because it is always called with the finger down in this situation. I'd use a Handler to delay starting the downloading by the animation time of your gallery (make sure to clear any delayed messages posted to this handler wheneveronItemSelected
is called or when you get anACTION_DOWN
event.Also be aware that the default Gallery component does not properly implement View recycling (it assumes each position in the adapter has a unique view, and also clears the recycler of these items when they go offscreen making it pretty pointless). Edit: on more looking it isn't pointless - but it's not a recycler in terms of next/previous views, rather it serves to avoid having to call
getView
for the current views during layout changes.This means the
convertView
parameter passed to yourgetView
method will more often that not be null, meaning you'll be inflating a lot of views (which is expensive) - see my answer to Does a replacement for Gallery with View recycling exist? for some hints on that. (PS: I have since modified that code - I would use a different recycle bin for layout phases and scroll phases, in the layout phase place and retrieve the views in the layout recycle bin according to their position, and DO NOT call getView if the view you get from the bin is non-null since it will be exactly the same view; also clear the layout recycle bin after the layout phase -- this makes things a bit more snappier)PS: Also be very careful with what you do in
OnItemSelected
- namely unless it's in the places mentioned above then try to do as little as possible. For instance I was setting some text in aTextView
above my Gallery inOnItemSelected
. Just moving this call into the same points as where I updated thumbnails made a noticable difference.我有一个答案给你!
当内部在
ImageView
上调用任何setImage...
方法时,会请求布局传递,例如上面的setImageBitmap()
被定义为这样的,它调用
具有画廊“捕捉”到当前最接近画廊中心的图像中心的效果。
为了防止这种情况,我所做的就是让加载到图库中的视图具有明确的高度和宽度(以
dip
为单位),并使用忽略布局请求的ImageView
子类。这是有效的,因为图库最初仍然有一个布局通道,但每次图库中的图像发生变化时都不需要这样做,我想只有当图库视图的宽度和高度设置为 WRAP_CONTENT< 时才需要发生这种情况/code>,我们没有。请注意,由于setImageDrawable()
中仍会调用invalidate()
,因此设置时仍会绘制图像。下面是我非常简单的
ImageView
子类!编辑:我应该提到 onItemSelected 方法也可以工作,但是由于我需要在进行投掷时挂钩,所以我想出了上面的方法,我认为这是更灵活的方法
I have an answer for you!
When any of the
setImage...
methods are called onImageView
in internally a layout pass is requested, for example,setImageBitmap()
as above is defined as suchwhich calls
which has the effect of the gallery 'snapping' to the center of the image thats currently closest to the center of the gallery.
What I have done to prevent this is have the View thats loading into the Gallery have a explicit height and width (in
dip
s) and using anImageView
subclass that ignores layout requests. This works as the gallery still has a layout pass initially but does not bother doing this every time an image in the gallery changes, which I imagine would only need to happen if the gallery views had their width and height set toWRAP_CONTENT
, which we dont. Note that asinvalidate()
is still called insetImageDrawable()
the image will still be drawn when set.My very simple
ImageView
subclass below!edit: i should mention that the onItemSelected approaches can also work, but as I needed to hook into that while flinging was taking place I came up with the above, which I think is more flexible approach
这可能是 Gallery 的 onLayout 方法中的错误。请查看 http://code.google.com/p/android/issues /detail?id=16171 了解可能的解决方法。
This might be a bug in the Gallery's onLayout method. Check out http://code.google.com/p/android/issues/detail?id=16171 for a possible workaround.