【发布时间】:2018-04-10 10:22:07
【问题描述】:
我有一个大约 5-6 行的 ListView,每一行都是一个显示水平片段的 RecycleView。卡片(例如 Netflix、HBO 应用)
注意事项:
- 每个 RecycleView 都有大约 30-50 个项目。
- 每个 ViewHolder 都有一个“大”图片作为背景(大约 300x260dp,具体取决于设备屏幕尺寸,宽度约为设备屏幕的 90%)
- 使用 Glide 加载图像(代码如下)
我为 ListView Adapter 中的 RecycleViews 创建了一个缓存(SparseArray),以保持每个 RecycleView 的状态,否则每次离开屏幕时它都会重新启动到位置 0。
通过这样做,只需将每个 RecycleView 滚动到最后,就会使其中一些冻结。
尝试了几种解决方法:
- 审查了 MAT 并重构了一些代码以避免由于静态上下文而泄漏
- MAT 显然会因为图像而显示大量内存
- 缩小图片尺寸
- 隐藏图片
- 创建 ViewHolders 缓存(无效)
- Glide 下载后为图像创建内存缓存
- 将 ViewHolders setIsRecyclable 设置为 false
但是,这些选项都不起作用,保证应用程序不会冻结的唯一方法是避免 ListView 缓存(如果您将视图移出屏幕,则会丢失您访问的最后一个位置)
Glide Code(我在这里尝试了几个选项:避免 Bitmap、避免内存缓存、将磁盘 Strategy 更新为 ALL)
Glide.with(context).load(url)
.asBitmap()
.skipMemoryCache(true)
.thumbnail(0.5f)
.centerCrop()
.error(placeHolderId)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
//.priority(Priority.NORMAL)
.dontAnimate()
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
if (bitmap != null) {
imgView.setImageBitmap(bitmap);
//cache.put(url, bitmap);
} else {
imgView.setBackground(null);
}
}
});
有什么建议/建议吗? 也许还有另一种选择可以在没有缓存的情况下保持 View 的状态?
ListView 适配器代码:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
case TYPE_VENUE_FILTER: {
View v;
// Create a new view into the list.
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null) {
v = inflater.inflate(R.layout.fragment_venue_filter, parent, false);
}
// We need to get the exact filter to be displayed
int index = position - FIRST_VENUE_FILTER_ROW;
if (index < this.filters.size()) {
VenueFilter filter = this.filters.get(index);
// Recalculate the distance order
filter.sortVenuesForLocation(this.lastLocation);
// Set data into the view.
VenueFilterFragment.loadFragment(this.context, v, filter, this.fm);
}
return v;
}
}
2004 年 11 月更新
我终于明白了。
- 按照建议重用适配器上的 converView
- 在 RecycleView 中包含一个 ScrollListener 并将最后一个滚动位置存储在 bean 中,以便在重新访问时恢复它。
此代码在适配器中,用于对特定行进行充气(getView)
// We should reset or recover the scroll state
LinearLayoutManager llm = (LinearLayoutManager) filterContent.getLayoutManager();
if (filter.offset > 0) {
if (llm != null) llm.scrollToPosition(filter.offset + 1);
} else {
if (llm != null) llm.scrollToPosition(0);
}
// We need to clear every time. Because we are reusing the view
recycleView.clearOnScrollListeners();
// We need to create a new specific scroll listener
recycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// Keeping the offset for a future load
LinearLayoutManager llm = (LinearLayoutManager) recyclerView.getLayoutManager();
if (llm != null) filter.offset = llm.findFirstVisibleItemPosition();
}
});
【问题讨论】:
-
请输入您的适配器代码
-
添加 ListView 适配器代码@CaoMinhVu,感谢关注
-
在我看来有两件事非常可疑:1)您根本没有使用
convertView,即您从不重用来自适配器的视图并且没有利用回收逻辑,这将给你的性能放缓。在这种情况下,您必须为每个项目增加视图,这是一项昂贵的操作。 2)参考java命名约定,我假设VenueFilterFragment是一个类名,因此loadFragment是一个奇怪的静态方法..这样做的目的是什么? -
我在 ListView 中对不同类型的单元格使用 Fragments,这个 Fragments 可以用于应用程序的其他部分。静态方法仅用于在给定视图上设置内容。 (但现在作为片段执行,因为我丢失了所有片段生命周期)。与第 1 点相关,您是对的,现在正在研究它。
-
这对我来说确实听起来很奇怪。如果要为给定的片段设置内容,它应该是片段对象上的非静态方法。静态方法在片段类上设置“所谓的”状态,这意味着它是某种共享数据。我看到您在每次
getView调用时都将一个过滤器对象传递给这个共享状态,这对我来说真的没有多大意义。我想不出任何正确的情况。请重新考虑这个逻辑,你很可能不想在那里进行静态调用。
标签: android listview android-recyclerview android-glide