Mastering RecyclerView Layouts

29
Mastering RecyclerView Layouts Dave Smith • NewCircle, Inc. @devunwired +DaveSmithDev

Transcript of Mastering RecyclerView Layouts

Page 1: Mastering RecyclerView Layouts

Mastering RecyclerView Layouts

Dave Smith • NewCircle, Inc. @devunwired +DaveSmithDev

Page 2: Mastering RecyclerView Layouts

Why Am I Here?

Help Reduce Sanitarium Admissions caused by RecyclerView Layouts.

Page 3: Mastering RecyclerView Layouts
Page 4: Mastering RecyclerView Layouts

RecyclerView recyclerView = new RecyclerView(this); recyclerView.setLayoutManager(…); setContentView(recyclerView);

Page 5: Mastering RecyclerView Layouts

LinearLayoutManager

LinearLayoutManager manager = new LinearLayoutManager( this, LinearLayoutManager.VERTICAL, false);

Page 6: Mastering RecyclerView Layouts

GridLayoutManager

GridLayoutManager manager = new GridLayoutManager( this, 2, /* Number of grid columns */ GridLayoutManager.VERTICAL, false);

Page 7: Mastering RecyclerView Layouts

GridLayoutManager

GridLayoutManager manager = new GridLayoutManager( this, 2, /* Number of grid columns */ GridLayoutManager.VERTICAL, false);

manager.setSpanSizeLookup( new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { //Stagger every other row return (position % 3 == 0 ? 2 : 1); } });

Page 8: Mastering RecyclerView Layouts

StaggeredGridLayoutManager

StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager( 2, /* Number of grid columns */ StaggeredGridLayoutManager.VERTICAL);

Page 9: Mastering RecyclerView Layouts

Case Study: FixedGridLayoutManager

1 2 3 4 5 6

7 8 9 10 11 12

13 14 15 16 17 18

19 20 21 22 23 24

25 26 27 28 29 30

31 32 33 34 35 36

Page 10: Mastering RecyclerView Layouts

The Recycler

LayoutManager Recycler

Adapter

Obtain View

Recycle View

Bind

Page 11: Mastering RecyclerView Layouts

The Recycler

LayoutManager Recycler

Adapter

Obtain View

Recycle View

Bind

Page 12: Mastering RecyclerView Layouts

The Recycler

Scrap Heap detachAndScrapView()

Recycle Pool removeAndRecycleView()

Page 13: Mastering RecyclerView Layouts

Handling Decorations

Item View

getDecoratedLeft() getDecoratedTop()

getDecoratedRight() getDecoratedBottom()

Page 14: Mastering RecyclerView Layouts

Handling Decorations

Item View

getDecoratedWidth()

getDecoratedHeight()

Page 15: Mastering RecyclerView Layouts

Handling Decorations

Item ViewlayoutDecorated() measureChild()

Page 16: Mastering RecyclerView Layouts

The Fill Technique

fillGaps() Discover first visible position/location Find layout gaps (at the edges) Scrap everything Lay out all visible positions

Save as little state as possible.

Is there space before the first visible position? Is there space after the last visible position?

Page 17: Mastering RecyclerView Layouts

The Fill Technique

private void fillGrid(int direction, …, RecyclerView.Recycler recycler, RecyclerView.State state) {

//…Obtain the first visible item position… detachAndScrapAttachedViews(recycler); for (…) { int nextPosition = …; View View = recycler.getViewForPosition(nextPosition); addView(view); measureChildWithMargins(view, …); layoutDecorated(view, …); } //Remove anything that is left behind

Page 18: Mastering RecyclerView Layouts

private void fillGrid(int direction, …, RecyclerView.Recycler recycler, RecyclerView.State state) {

//…Obtain the first visible item position… detachAndScrapAttachedViews(recycler); for (…) { int nextPosition = …; View View = recycler.getViewForPosition(nextPosition); addView(view); measureChildWithMargins(view, …); layoutDecorated(view, …); } //Remove anything that is left behind final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList(); for (int i=0; i < scrapList.size(); i++) { final View removingView = scrapList.get(i); recycler.recycleView(removingView); } }

Page 19: Mastering RecyclerView Layouts

0 1

2 3

4 5

6 7

Use Attach/Detach to quickly

reorder view indices.

Page 20: Mastering RecyclerView Layouts

Level 1: Make It Work

onLayoutChildren() Run a FILL operation

canScrollVertically() canScrollHorizontally() Which axes enable scrolling?

scrollHorizontallyBy() scrollVerticallyBy() Clamp supplied delta against boundary conditions Shift all views in the layout Trigger a FILL operation Report back actual distance scrolled

Page 21: Mastering RecyclerView Layouts

Level 1: Make It Workpublic int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { … int delta; if (dx > 0) { // Contents are scrolling left delta = …; } else { // Contents are scrolling right delta = …; } offsetChildrenHorizontal(delta); if (dx > 0) { fillGrid(DIRECTION_START, …, recycler, state); } else { fillGrid(DIRECTION_END, …, recycler, state); } return -delta; }

Page 22: Mastering RecyclerView Layouts

Level 2: Data Set Changes

onAdapterChanged() removeAllViews()

notifyDataSetChanged() -> onLayoutChanged() Treat as a new layout, and just run a FILL…

Page 23: Mastering RecyclerView Layouts

Level 3: Add Some Flair

scrollToPosition() Track Requested Position Trigger requestLayout()

smoothScrollToPosition() Create a SmoothScroller instance Set the target position Invoke startSmoothScroll()

Page 24: Mastering RecyclerView Layouts

Level 3: Add Some Flair

LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { @Override public PointF computeScrollVectorForPosition(int targetPosition) { final int rowOffset = …; final int columnOffset = …; return new PointF(columnOffset * stepWidth, rowOffset * stepHeight); } }; scroller.setTargetPosition(position); startSmoothScroll(scroller);

Page 25: Mastering RecyclerView Layouts

Level 4: Zen Master

supportsPredictiveItemAnimations() We have more to say about animations…

onLayoutChildren() [isPreLayout() == true] Note any removed views -> LayoutParams.isViewRemoved() Add extra views during FILL to cover space left behind

onLayoutChildren() Place disappearing views off-screen

Page 26: Mastering RecyclerView Layouts

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { … if (state.isPreLayout()) { final View child = getChildAt(…); final LayoutParams lp = (LayoutParams) child.getLayoutParams();

if (lp.isItemRemoved()) { //Track and count view removals… } } //Clear all attached views into the recycle bin detachAndScrapAttachedViews(recycler); //Fill the grid for the initial layout of views fillGrid(…); //Anything left? Lay out for disappearing animation if (!state.isPreLayout() && !recycler.getScrapList().isEmpty()) { final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();

//Laying out a scrap item removes it from the list… //Beware concurrent modification! … }}

Page 27: Mastering RecyclerView Layouts

Default Item Animations

Page 28: Mastering RecyclerView Layouts

Predictive Item Animations

Page 29: Mastering RecyclerView Layouts

Resources

RecyclerView Playground https://github.com/devunwired/recyclerview-playground

Building a RecyclerView LayoutManager http://wiresareobsolete.com/ Redux - Handling Disconnected Ranges

Android SDK Reference https://developer.android.com/training/material/lists-cards.html