I have a grid with steps containing 370 elements, with images.
I want the elements to be recycled quickly to be careful with the memory, but a ViewHolder is created and then attached to each element in my adapter and does not pay attention to whether children are visible.
I tried the following
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
The onCreateViewHolder and onBindViewHolder log displays are called 185 times each. Then onViewRecycled is called 185 times before resuming calls to onCreateViewHolder until we reach full 370.
This may be a problem of understanding on my part, but I think that RecyclerView should only link the views that are visible, or honor only 20 views or 20 in the + pool, although many fit on the screen. How to do it using StaggeredGridLayoutManager?
If I listen to scroll changes and use findFirstCompletelyVisibleItemPositions and findLastCompletelyVisibleItemPositions, this still covers every element in the adapter, not just 6 that fit on the screen
My adapter code
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 } }
Layout structure of my activity
<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>
Fragment Layout
<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" /> </RelativeLayout> </android.support.v4.widget.NestedScrollView>
android layout-manager android-recyclerview
Nick Cardoso Nov 01 '16 at 0:08 2016-11-01 00:08
source share