我有一个包含370个项目的交错网格,带有图像。
我想确保快速回收这些项目以小心内存,但是会创建一个ViewHolder,然后绑定到适配器中的每个项目,并且不关注子项是否可见
我已尝试过以下
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
rv.setLayoutManager(lm);
rv.setItemViewCacheSize(20); //Has no effect
RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
pool.setMaxRecycledViews(0, 20);
rv.setRecycledViewPool(pool); //also has no effect
日志显示onCreateViewHolder和onBindViewHolder每次调用185次。然后在恢复对onCreateViewHolder的调用之前调用onViewRecycled 185次,直到我们达到完整的370。
这对我来说可能是一个理解问题,但我认为RecyclerView应该仅绑定那些可见的视图,或仅仅支持20个视图,或者20个池中的20个,但是很多适合屏幕。如何使用StaggeredGridLayoutManager实现这一点?
如果我听滚动更改并使用findFirstCompletelyVisibleItemPositions和findLastCompletelyVisibleItemPositions,这仍然会跨越适配器中的每一项,而不仅仅是适合屏幕的6项
我的适配器代码
class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
static final int NUM_COLS = 3;
private final LayoutInflater mInflater;
private final List<GridItem> mEntries;
private int mLastExpanded; //stores where the last expanded item was
private OnCardClickListener mOnItemClick;
MyAdapter(Context context) {
super();
mEntries = new ArrayList<>();
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
void setOnTileClickListener(@Nullable OnCardClickListener listener) {
mOnItemClick = listener;
notifyDataSetChanged(); //recall bind logic
}
void setItems(Collection<GridItem> items) {
mEntries.clear();
mEntries.addAll(items);
sort();
}
@WorkerThread
private void sort() {
Collections.sort(mEntries, (thisEntry, otherEntry) -> {
int ret;
if (otherEntry == null || thisEntry.getCreated() == otherEntry.getCreated()) {
ret = 0;
} else if (thisEntry.getCreated() > otherEntry.getCreated()) {
ret = -1;
} else {
ret = 1;
}
return ret;
});
}
@Override
public int getItemCount() {
return mEntries.size();
}
private GridItem getItem(int position) {
return mEntries.get(position);
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(mInflater.inflate(R.layout.li_grid_item, parent, false));
}
@Override
public void onViewRecycled(MyViewHolder holder) {
super.onViewRecycled(holder);
holder.onViewRecycled(); //clears bitmap reference
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
determineTileSize(holder, position);
holder.bind(getItem(position), mOnItemClick);
}
private void determineTileSize(MyViewHolder holder, int position) {
ViewGroup.LayoutParams cardParams = holder.getCardLayout().getLayoutParams();
StaggeredGridLayoutManager.LayoutParams gridItemParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
if (shouldBeExpanded(position)) {
cardParams.height = (int) holder.getCard().getResources().getDimension(R.dimen.spacing_card_large);
mLastExpanded = position;
gridItemParams.setFullSpan(true);
}
holder.getCardLayout().setLayoutParams(cardParams);
}
private boolean shouldBeExpanded(int position) {
return position > (mLastExpanded + NUM_COLS); //minimum 1 row between enlarged
}
}
我的活动布局结构
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" ...>
<android.support.design.widget.AppBarLayout ...>
<android.support.design.widget.CollapsingToolbarLayout ...>
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" ... />
<android.support.design.widget.TabLayout ...
app:layout_collapseMode="pin"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<FrameLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="@dimen/height_backdrop"
android:minHeight="@dimen/height_backdrop"
android:background="@color/colorAccent"
android:visibility="gone"
app:elevation="@dimen/spacing_narrow"
app:behavior_peekHeight="0dp"
app:behavior_hideable="true"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
</android.support.design.widget.CoordinatorLayout>
片段布局
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" ...>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/grid_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
<!-- Empty and loading views -->
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
答案 0 :(得分:7)
您遇到此问题的原因是您在RecyclerView
中添加了NestedScrollView
。
这不是我第一次听说这个问题,我和可能每个试图将RecyclerView
放入NestedScrollView
的人都面临这个问题(如果注意到的话)。
据我所知,原因是因为当你RecyclerView
放置NestedScrollView
时,它无法确定RecyclerView
所需的确切高度。通常情况下,开发人员为此(简单来说)假设一旦所有上述视图都离开屏幕,RecyclerView高度应该是match_parent。但不幸的是,这是 NOT 的情况。
它以RecyclerView
以某种方式将wrap_content添加到其所有视图然后测量其高度(如果我错了,请纠正我)。不确定可能的错误或预期的行为,但我相信NestedScrollView
应该能够明确处理这种情况,否则,在RecyclerView
中添加NestedScrollView
是完全没用的,因为它不会回收视图,完全摧毁RecyclerView
概念,从而消耗大量内存。
只需从RecyclerView
移除NestedScrollView
,即可正确重复使用这些视图。
注意:答案可能不是100%正确,因为它完全基于我的个人观察和经验。任何更好的解决方案或改进答案都表示赞赏。
答案 1 :(得分:-1)
问题出在NestedScrollview
中,recyclerview
的可用空间尚未确定。
您可以使用android:fillViewport="true"
使NestedScrollView
测量RecyclerView
。 RecyclerView
将填充剩余的高度。因此,如果您想滚动NestScrollView
,则可以设置RecyclerView的minHeight。