1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package androidx.leanback.widget;
15 
16 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
17 
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.graphics.Rect;
21 import android.util.AttributeSet;
22 import android.view.Gravity;
23 import android.view.KeyEvent;
24 import android.view.MotionEvent;
25 import android.view.View;
26 
27 import androidx.annotation.RestrictTo;
28 import androidx.leanback.R;
29 import androidx.recyclerview.widget.RecyclerView;
30 import androidx.recyclerview.widget.SimpleItemAnimator;
31 
32 /**
33  * An abstract base class for vertically and horizontally scrolling lists. The items come
34  * from the {@link RecyclerView.Adapter} associated with this view.
35  * Do not directly use this class, use {@link VerticalGridView} and {@link HorizontalGridView}.
36  * The class is not intended to be subclassed other than {@link VerticalGridView} and
37  * {@link HorizontalGridView}.
38  */
39 public abstract class BaseGridView extends RecyclerView {
40 
41     /**
42      * Always keep focused item at a aligned position.  Developer can use
43      * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
44      * In this mode, the last focused position will be remembered and restored when focus
45      * is back to the view.
46      * @hide
47      */
48     @RestrictTo(LIBRARY_GROUP)
49     public final static int FOCUS_SCROLL_ALIGNED = 0;
50 
51     /**
52      * Scroll to make the focused item inside client area.
53      * @hide
54      */
55     @RestrictTo(LIBRARY_GROUP)
56     public final static int FOCUS_SCROLL_ITEM = 1;
57 
58     /**
59      * Scroll a page of items when focusing to item outside the client area.
60      * The page size matches the client area size of RecyclerView.
61      * @hide
62      */
63     @RestrictTo(LIBRARY_GROUP)
64     public final static int FOCUS_SCROLL_PAGE = 2;
65 
66     /**
67      * The first item is aligned with the low edge of the viewport. When
68      * navigating away from the first item, the focus item is aligned to a key line location.
69      * <p>
70      * For HorizontalGridView, low edge refers to getPaddingLeft() when RTL is false or
71      * getWidth() - getPaddingRight() when RTL is true.
72      * For VerticalGridView, low edge refers to getPaddingTop().
73      * <p>
74      * The key line location is calculated by "windowAlignOffset" and
75      * "windowAlignOffsetPercent"; if neither of these two is defined, the
76      * default value is 1/2 of the size.
77      * <p>
78      * Note if there are very few items between low edge and key line, use
79      * {@link #setWindowAlignmentPreferKeyLineOverLowEdge(boolean)} to control whether you prefer
80      * to align the items to key line or low edge. Default is preferring low edge.
81      */
82     public final static int WINDOW_ALIGN_LOW_EDGE = 1;
83 
84     /**
85      * The last item is aligned with the high edge of the viewport when
86      * navigating to the end of list. When navigating away from the end, the
87      * focus item is aligned to a key line location.
88      * <p>
89      * For HorizontalGridView, high edge refers to getWidth() - getPaddingRight() when RTL is false
90      * or getPaddingLeft() when RTL is true.
91      * For VerticalGridView, high edge refers to getHeight() - getPaddingBottom().
92      * <p>
93      * The key line location is calculated by "windowAlignOffset" and
94      * "windowAlignOffsetPercent"; if neither of these two is defined, the
95      * default value is 1/2 of the size.
96      * <p>
97      * Note if there are very few items between high edge and key line, use
98      * {@link #setWindowAlignmentPreferKeyLineOverHighEdge(boolean)} to control whether you prefer
99      * to align the items to key line or high edge. Default is preferring key line.
100      */
101     public final static int WINDOW_ALIGN_HIGH_EDGE = 1 << 1;
102 
103     /**
104      * The first item and last item are aligned with the two edges of the
105      * viewport. When navigating in the middle of list, the focus maintains a
106      * key line location.
107      * <p>
108      * The key line location is calculated by "windowAlignOffset" and
109      * "windowAlignOffsetPercent"; if neither of these two is defined, the
110      * default value is 1/2 of the size.
111      */
112     public final static int WINDOW_ALIGN_BOTH_EDGE =
113             WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;
114 
115     /**
116      * The focused item always stays in a key line location.
117      * <p>
118      * The key line location is calculated by "windowAlignOffset" and
119      * "windowAlignOffsetPercent"; if neither of these two is defined, the
120      * default value is 1/2 of the size.
121      */
122     public final static int WINDOW_ALIGN_NO_EDGE = 0;
123 
124     /**
125      * Value indicates that percent is not used.
126      */
127     public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1;
128 
129     /**
130      * Value indicates that percent is not used.
131      */
132     public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED =
133             ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
134 
135     /**
136      * Dont save states of any child views.
137      */
138     public static final int SAVE_NO_CHILD = 0;
139 
140     /**
141      * Only save on screen child views, the states are lost when they become off screen.
142      */
143     public static final int SAVE_ON_SCREEN_CHILD = 1;
144 
145     /**
146      * Save on screen views plus save off screen child views states up to
147      * {@link #getSaveChildrenLimitNumber()}.
148      */
149     public static final int SAVE_LIMITED_CHILD = 2;
150 
151     /**
152      * Save on screen views plus save off screen child views without any limitation.
153      * This might cause out of memory, only use it when you are dealing with limited data.
154      */
155     public static final int SAVE_ALL_CHILD = 3;
156 
157     /**
158      * Listener for intercepting touch dispatch events.
159      */
160     public interface OnTouchInterceptListener {
161         /**
162          * Returns true if the touch dispatch event should be consumed.
163          */
onInterceptTouchEvent(MotionEvent event)164         public boolean onInterceptTouchEvent(MotionEvent event);
165     }
166 
167     /**
168      * Listener for intercepting generic motion dispatch events.
169      */
170     public interface OnMotionInterceptListener {
171         /**
172          * Returns true if the touch dispatch event should be consumed.
173          */
onInterceptMotionEvent(MotionEvent event)174         public boolean onInterceptMotionEvent(MotionEvent event);
175     }
176 
177     /**
178      * Listener for intercepting key dispatch events.
179      */
180     public interface OnKeyInterceptListener {
181         /**
182          * Returns true if the key dispatch event should be consumed.
183          */
onInterceptKeyEvent(KeyEvent event)184         public boolean onInterceptKeyEvent(KeyEvent event);
185     }
186 
187     public interface OnUnhandledKeyListener {
188         /**
189          * Returns true if the key event should be consumed.
190          */
onUnhandledKey(KeyEvent event)191         public boolean onUnhandledKey(KeyEvent event);
192     }
193 
194     final GridLayoutManager mLayoutManager;
195 
196     /**
197      * Animate layout changes from a child resizing or adding/removing a child.
198      */
199     private boolean mAnimateChildLayout = true;
200 
201     private boolean mHasOverlappingRendering = true;
202 
203     private RecyclerView.ItemAnimator mSavedItemAnimator;
204 
205     private OnTouchInterceptListener mOnTouchInterceptListener;
206     private OnMotionInterceptListener mOnMotionInterceptListener;
207     private OnKeyInterceptListener mOnKeyInterceptListener;
208     RecyclerView.RecyclerListener mChainedRecyclerListener;
209     private OnUnhandledKeyListener mOnUnhandledKeyListener;
210 
211     /**
212      * Number of items to prefetch when first coming on screen with new data.
213      */
214     int mInitialPrefetchItemCount = 4;
215 
BaseGridView(Context context, AttributeSet attrs, int defStyle)216     BaseGridView(Context context, AttributeSet attrs, int defStyle) {
217         super(context, attrs, defStyle);
218         mLayoutManager = new GridLayoutManager(this);
219         setLayoutManager(mLayoutManager);
220         // leanback LayoutManager already restores focus inside onLayoutChildren().
221         setPreserveFocusAfterLayout(false);
222         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
223         setHasFixedSize(true);
224         setChildrenDrawingOrderEnabled(true);
225         setWillNotDraw(true);
226         setOverScrollMode(View.OVER_SCROLL_NEVER);
227         // Disable change animation by default on leanback.
228         // Change animation will create a new view and cause undesired
229         // focus animation between the old view and new view.
230         ((SimpleItemAnimator)getItemAnimator()).setSupportsChangeAnimations(false);
231         super.setRecyclerListener(new RecyclerView.RecyclerListener() {
232             @Override
233             public void onViewRecycled(RecyclerView.ViewHolder holder) {
234                 mLayoutManager.onChildRecycled(holder);
235                 if (mChainedRecyclerListener != null) {
236                     mChainedRecyclerListener.onViewRecycled(holder);
237                 }
238             }
239         });
240     }
241 
initBaseGridViewAttributes(Context context, AttributeSet attrs)242     void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
243         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
244         boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
245         boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false);
246         mLayoutManager.setFocusOutAllowed(throughFront, throughEnd);
247         boolean throughSideStart = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideStart, true);
248         boolean throughSideEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideEnd, true);
249         mLayoutManager.setFocusOutSideAllowed(throughSideStart, throughSideEnd);
250         mLayoutManager.setVerticalSpacing(
251                 a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_verticalSpacing,
252                         a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)));
253         mLayoutManager.setHorizontalSpacing(
254                 a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_horizontalSpacing,
255                         a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)));
256         if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
257             setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
258         }
259         a.recycle();
260     }
261 
262     /**
263      * Sets the strategy used to scroll in response to item focus changing:
264      * <ul>
265      * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
266      * <li>{@link #FOCUS_SCROLL_ITEM}</li>
267      * <li>{@link #FOCUS_SCROLL_PAGE}</li>
268      * </ul>
269      * @hide
270      */
271     @RestrictTo(LIBRARY_GROUP)
setFocusScrollStrategy(int scrollStrategy)272     public void setFocusScrollStrategy(int scrollStrategy) {
273         if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM
274             && scrollStrategy != FOCUS_SCROLL_PAGE) {
275             throw new IllegalArgumentException("Invalid scrollStrategy");
276         }
277         mLayoutManager.setFocusScrollStrategy(scrollStrategy);
278         requestLayout();
279     }
280 
281     /**
282      * Returns the strategy used to scroll in response to item focus changing.
283      * <ul>
284      * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
285      * <li>{@link #FOCUS_SCROLL_ITEM}</li>
286      * <li>{@link #FOCUS_SCROLL_PAGE}</li>
287      * </ul>
288      * @hide
289      */
290     @RestrictTo(LIBRARY_GROUP)
getFocusScrollStrategy()291     public int getFocusScrollStrategy() {
292         return mLayoutManager.getFocusScrollStrategy();
293     }
294 
295     /**
296      * Sets the method for focused item alignment in the view.
297      *
298      * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE},
299      *        {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or
300      *        {@link #WINDOW_ALIGN_NO_EDGE}.
301      */
setWindowAlignment(int windowAlignment)302     public void setWindowAlignment(int windowAlignment) {
303         mLayoutManager.setWindowAlignment(windowAlignment);
304         requestLayout();
305     }
306 
307     /**
308      * Returns the method for focused item alignment in the view.
309      *
310      * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE},
311      *         {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}.
312      */
getWindowAlignment()313     public int getWindowAlignment() {
314         return mLayoutManager.getWindowAlignment();
315     }
316 
317     /**
318      * Sets whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
319      * When true, if there are very few items between low edge and key line, align items to key
320      * line instead of align items to low edge.
321      * Default value is false (aka prefer align to low edge).
322      *
323      * @param preferKeyLineOverLowEdge True to prefer key line over low edge, false otherwise.
324      */
setWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge)325     public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge) {
326         mLayoutManager.mWindowAlignment.mainAxis()
327                 .setPreferKeylineOverLowEdge(preferKeyLineOverLowEdge);
328         requestLayout();
329     }
330 
331 
332     /**
333      * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
334      * When true, if there are very few items between high edge and key line, align items to key
335      * line instead of align items to high edge.
336      * Default value is true (aka prefer align to key line).
337      *
338      * @param preferKeyLineOverHighEdge True to prefer key line over high edge, false otherwise.
339      */
setWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge)340     public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge) {
341         mLayoutManager.mWindowAlignment.mainAxis()
342                 .setPreferKeylineOverHighEdge(preferKeyLineOverHighEdge);
343         requestLayout();
344     }
345 
346     /**
347      * Returns whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
348      * When true, if there are very few items between low edge and key line, align items to key
349      * line instead of align items to low edge.
350      * Default value is false (aka prefer align to low edge).
351      *
352      * @return True to prefer key line over low edge, false otherwise.
353      */
isWindowAlignmentPreferKeyLineOverLowEdge()354     public boolean isWindowAlignmentPreferKeyLineOverLowEdge() {
355         return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverLowEdge();
356     }
357 
358 
359     /**
360      * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
361      * When true, if there are very few items between high edge and key line, align items to key
362      * line instead of align items to high edge.
363      * Default value is true (aka prefer align to key line).
364      *
365      * @return True to prefer key line over high edge, false otherwise.
366      */
isWindowAlignmentPreferKeyLineOverHighEdge()367     public boolean isWindowAlignmentPreferKeyLineOverHighEdge() {
368         return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverHighEdge();
369     }
370 
371 
372     /**
373      * Sets the offset in pixels for window alignment key line.
374      *
375      * @param offset The number of pixels to offset.  If the offset is positive,
376      *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
377      *        if the offset is negative, the absolute value is distance from high
378      *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
379      *        Default value is 0.
380      */
setWindowAlignmentOffset(int offset)381     public void setWindowAlignmentOffset(int offset) {
382         mLayoutManager.setWindowAlignmentOffset(offset);
383         requestLayout();
384     }
385 
386     /**
387      * Returns the offset in pixels for window alignment key line.
388      *
389      * @return The number of pixels to offset.  If the offset is positive,
390      *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
391      *        if the offset is negative, the absolute value is distance from high
392      *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
393      *        Default value is 0.
394      */
getWindowAlignmentOffset()395     public int getWindowAlignmentOffset() {
396         return mLayoutManager.getWindowAlignmentOffset();
397     }
398 
399     /**
400      * Sets the offset percent for window alignment key line in addition to {@link
401      * #getWindowAlignmentOffset()}.
402      *
403      * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
404      *        width from low edge. Use
405      *        {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
406      *         Default value is 50.
407      */
setWindowAlignmentOffsetPercent(float offsetPercent)408     public void setWindowAlignmentOffsetPercent(float offsetPercent) {
409         mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent);
410         requestLayout();
411     }
412 
413     /**
414      * Returns the offset percent for window alignment key line in addition to
415      * {@link #getWindowAlignmentOffset()}.
416      *
417      * @return Percentage to offset. E.g., 40 means 40% of the width from the
418      *         low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if
419      *         disabled. Default value is 50.
420      */
getWindowAlignmentOffsetPercent()421     public float getWindowAlignmentOffsetPercent() {
422         return mLayoutManager.getWindowAlignmentOffsetPercent();
423     }
424 
425     /**
426      * Sets number of pixels to the end of low edge. Supports right to left layout direction.
427      * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
428      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
429      *
430      * @param offset In left to right or vertical case, it's the offset added to left/top edge.
431      *               In right to left case, it's the offset subtracted from right edge.
432      */
setItemAlignmentOffset(int offset)433     public void setItemAlignmentOffset(int offset) {
434         mLayoutManager.setItemAlignmentOffset(offset);
435         requestLayout();
436     }
437 
438     /**
439      * Returns number of pixels to the end of low edge. Supports right to left layout direction. In
440      * left to right or vertical case, it's the offset added to left/top edge. In right to left
441      * case, it's the offset subtracted from right edge.
442      * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
443      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
444      *
445      * @return The number of pixels to the end of low edge.
446      */
getItemAlignmentOffset()447     public int getItemAlignmentOffset() {
448         return mLayoutManager.getItemAlignmentOffset();
449     }
450 
451     /**
452      * Sets whether applies padding to item alignment when {@link #getItemAlignmentOffsetPercent()}
453      * is 0 or 100.
454      * <p>When true:
455      * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
456      * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
457      * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
458      * </p>
459      * <p>When false: does not apply padding</p>
460      */
setItemAlignmentOffsetWithPadding(boolean withPadding)461     public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
462         mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
463         requestLayout();
464     }
465 
466     /**
467      * Returns true if applies padding to item alignment when
468      * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
469      * <p>When true:
470      * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
471      * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
472      * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
473      * </p>
474      * <p>When false: does not apply padding</p>
475      */
isItemAlignmentOffsetWithPadding()476     public boolean isItemAlignmentOffsetWithPadding() {
477         return mLayoutManager.isItemAlignmentOffsetWithPadding();
478     }
479 
480     /**
481      * Sets the offset percent for item alignment in addition to {@link
482      * #getItemAlignmentOffset()}.
483      * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
484      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
485      *
486      * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
487      *        width from the low edge. Use
488      *        {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
489      */
setItemAlignmentOffsetPercent(float offsetPercent)490     public void setItemAlignmentOffsetPercent(float offsetPercent) {
491         mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent);
492         requestLayout();
493     }
494 
495     /**
496      * Returns the offset percent for item alignment in addition to {@link
497      * #getItemAlignmentOffset()}.
498      *
499      * @return Percentage to offset. E.g., 40 means 40% of the width from the
500      *         low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if
501      *         disabled. Default value is 50.
502      */
getItemAlignmentOffsetPercent()503     public float getItemAlignmentOffsetPercent() {
504         return mLayoutManager.getItemAlignmentOffsetPercent();
505     }
506 
507     /**
508      * Sets the id of the view to align with. Use {@link android.view.View#NO_ID} (default)
509      * for the root {@link RecyclerView.ViewHolder#itemView}.
510      * Item alignment settings on BaseGridView are if {@link ItemAlignmentFacet}
511      * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
512      */
setItemAlignmentViewId(int viewId)513     public void setItemAlignmentViewId(int viewId) {
514         mLayoutManager.setItemAlignmentViewId(viewId);
515     }
516 
517     /**
518      * Returns the id of the view to align with, or {@link android.view.View#NO_ID} for the root
519      * {@link RecyclerView.ViewHolder#itemView}.
520      * @return The id of the view to align with, or {@link android.view.View#NO_ID} for the root
521      * {@link RecyclerView.ViewHolder#itemView}.
522      */
getItemAlignmentViewId()523     public int getItemAlignmentViewId() {
524         return mLayoutManager.getItemAlignmentViewId();
525     }
526 
527     /**
528      * Sets the spacing in pixels between two child items.
529      * @deprecated use {@link #setItemSpacing(int)}
530      */
531     @Deprecated
setItemMargin(int margin)532     public void setItemMargin(int margin) {
533         setItemSpacing(margin);
534     }
535 
536     /**
537      * Sets the vertical and horizontal spacing in pixels between two child items.
538      * @param spacing Vertical and horizontal spacing in pixels between two child items.
539      */
setItemSpacing(int spacing)540     public void setItemSpacing(int spacing) {
541         mLayoutManager.setItemSpacing(spacing);
542         requestLayout();
543     }
544 
545     /**
546      * Sets the spacing in pixels between two child items vertically.
547      * @deprecated Use {@link #setVerticalSpacing(int)}
548      */
549     @Deprecated
setVerticalMargin(int margin)550     public void setVerticalMargin(int margin) {
551         setVerticalSpacing(margin);
552     }
553 
554     /**
555      * Returns the spacing in pixels between two child items vertically.
556      * @deprecated Use {@link #getVerticalSpacing()}
557      */
558     @Deprecated
getVerticalMargin()559     public int getVerticalMargin() {
560         return mLayoutManager.getVerticalSpacing();
561     }
562 
563     /**
564      * Sets the spacing in pixels between two child items horizontally.
565      * @deprecated Use {@link #setHorizontalSpacing(int)}
566      */
567     @Deprecated
setHorizontalMargin(int margin)568     public void setHorizontalMargin(int margin) {
569         setHorizontalSpacing(margin);
570     }
571 
572     /**
573      * Returns the spacing in pixels between two child items horizontally.
574      * @deprecated Use {@link #getHorizontalSpacing()}
575      */
576     @Deprecated
getHorizontalMargin()577     public int getHorizontalMargin() {
578         return mLayoutManager.getHorizontalSpacing();
579     }
580 
581     /**
582      * Sets the vertical spacing in pixels between two child items.
583      * @param spacing Vertical spacing between two child items.
584      */
setVerticalSpacing(int spacing)585     public void setVerticalSpacing(int spacing) {
586         mLayoutManager.setVerticalSpacing(spacing);
587         requestLayout();
588     }
589 
590     /**
591      * Returns the vertical spacing in pixels between two child items.
592      * @return The vertical spacing in pixels between two child items.
593      */
getVerticalSpacing()594     public int getVerticalSpacing() {
595         return mLayoutManager.getVerticalSpacing();
596     }
597 
598     /**
599      * Sets the horizontal spacing in pixels between two child items.
600      * @param spacing Horizontal spacing in pixels between two child items.
601      */
setHorizontalSpacing(int spacing)602     public void setHorizontalSpacing(int spacing) {
603         mLayoutManager.setHorizontalSpacing(spacing);
604         requestLayout();
605     }
606 
607     /**
608      * Returns the horizontal spacing in pixels between two child items.
609      * @return The Horizontal spacing in pixels between two child items.
610      */
getHorizontalSpacing()611     public int getHorizontalSpacing() {
612         return mLayoutManager.getHorizontalSpacing();
613     }
614 
615     /**
616      * Registers a callback to be invoked when an item in BaseGridView has
617      * been laid out.
618      *
619      * @param listener The listener to be invoked.
620      */
setOnChildLaidOutListener(OnChildLaidOutListener listener)621     public void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
622         mLayoutManager.setOnChildLaidOutListener(listener);
623     }
624 
625     /**
626      * Registers a callback to be invoked when an item in BaseGridView has
627      * been selected.  Note that the listener may be invoked when there is a
628      * layout pending on the view, affording the listener an opportunity to
629      * adjust the upcoming layout based on the selection state.
630      *
631      * @param listener The listener to be invoked.
632      */
setOnChildSelectedListener(OnChildSelectedListener listener)633     public void setOnChildSelectedListener(OnChildSelectedListener listener) {
634         mLayoutManager.setOnChildSelectedListener(listener);
635     }
636 
637     /**
638      * Registers a callback to be invoked when an item in BaseGridView has
639      * been selected.  Note that the listener may be invoked when there is a
640      * layout pending on the view, affording the listener an opportunity to
641      * adjust the upcoming layout based on the selection state.
642      * This method will clear all existing listeners added by
643      * {@link #addOnChildViewHolderSelectedListener}.
644      *
645      * @param listener The listener to be invoked.
646      */
setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)647     public void setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
648         mLayoutManager.setOnChildViewHolderSelectedListener(listener);
649     }
650 
651     /**
652      * Registers a callback to be invoked when an item in BaseGridView has
653      * been selected.  Note that the listener may be invoked when there is a
654      * layout pending on the view, affording the listener an opportunity to
655      * adjust the upcoming layout based on the selection state.
656      *
657      * @param listener The listener to be invoked.
658      */
addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)659     public void addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
660         mLayoutManager.addOnChildViewHolderSelectedListener(listener);
661     }
662 
663     /**
664      * Remove the callback invoked when an item in BaseGridView has been selected.
665      *
666      * @param listener The listener to be removed.
667      */
removeOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)668     public void removeOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)
669             {
670         mLayoutManager.removeOnChildViewHolderSelectedListener(listener);
671     }
672 
673     /**
674      * Changes the selected item immediately without animation.
675      */
setSelectedPosition(int position)676     public void setSelectedPosition(int position) {
677         mLayoutManager.setSelection(position, 0);
678     }
679 
680     /**
681      * Changes the selected item and/or subposition immediately without animation.
682      * @hide
683      */
684     @RestrictTo(LIBRARY_GROUP)
setSelectedPositionWithSub(int position, int subposition)685     public void setSelectedPositionWithSub(int position, int subposition) {
686         mLayoutManager.setSelectionWithSub(position, subposition, 0);
687     }
688 
689     /**
690      * Changes the selected item immediately without animation, scrollExtra is
691      * applied in primary scroll direction.  The scrollExtra will be kept until
692      * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
693      */
setSelectedPosition(int position, int scrollExtra)694     public void setSelectedPosition(int position, int scrollExtra) {
695         mLayoutManager.setSelection(position, scrollExtra);
696     }
697 
698     /**
699      * Changes the selected item and/or subposition immediately without animation, scrollExtra is
700      * applied in primary scroll direction.  The scrollExtra will be kept until
701      * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
702      * @hide
703      */
704     @RestrictTo(LIBRARY_GROUP)
setSelectedPositionWithSub(int position, int subposition, int scrollExtra)705     public void setSelectedPositionWithSub(int position, int subposition, int scrollExtra) {
706         mLayoutManager.setSelectionWithSub(position, subposition, scrollExtra);
707     }
708 
709     /**
710      * Changes the selected item and run an animation to scroll to the target
711      * position.
712      * @param position Adapter position of the item to select.
713      */
setSelectedPositionSmooth(int position)714     public void setSelectedPositionSmooth(int position) {
715         mLayoutManager.setSelectionSmooth(position);
716     }
717 
718     /**
719      * Changes the selected item and/or subposition, runs an animation to scroll to the target
720      * position.
721      * @hide
722      */
723     @RestrictTo(LIBRARY_GROUP)
setSelectedPositionSmoothWithSub(int position, int subposition)724     public void setSelectedPositionSmoothWithSub(int position, int subposition) {
725         mLayoutManager.setSelectionSmoothWithSub(position, subposition);
726     }
727 
728     /**
729      * Perform a task on ViewHolder at given position after smooth scrolling to it.
730      * @param position Position of item in adapter.
731      * @param task Task to executed on the ViewHolder at a given position.
732      */
setSelectedPositionSmooth(final int position, final ViewHolderTask task)733     public void setSelectedPositionSmooth(final int position, final ViewHolderTask task) {
734         if (task != null) {
735             RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
736             if (vh == null || hasPendingAdapterUpdates()) {
737                 addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
738                     @Override
739                     public void onChildViewHolderSelected(RecyclerView parent,
740                             RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
741                         if (selectedPosition == position) {
742                             removeOnChildViewHolderSelectedListener(this);
743                             task.run(child);
744                         }
745                     }
746                 });
747             } else {
748                 task.run(vh);
749             }
750         }
751         setSelectedPositionSmooth(position);
752     }
753 
754     /**
755      * Perform a task on ViewHolder at given position after scroll to it.
756      * @param position Position of item in adapter.
757      * @param task Task to executed on the ViewHolder at a given position.
758      */
setSelectedPosition(final int position, final ViewHolderTask task)759     public void setSelectedPosition(final int position, final ViewHolderTask task) {
760         if (task != null) {
761             RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
762             if (vh == null || hasPendingAdapterUpdates()) {
763                 addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
764                     @Override
765                     public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
766                             RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
767                         if (selectedPosition == position) {
768                             removeOnChildViewHolderSelectedListener(this);
769                             task.run(child);
770                         }
771                     }
772                 });
773             } else {
774                 task.run(vh);
775             }
776         }
777         setSelectedPosition(position);
778     }
779 
780     /**
781      * Returns the adapter position of selected item.
782      * @return The adapter position of selected item.
783      */
getSelectedPosition()784     public int getSelectedPosition() {
785         return mLayoutManager.getSelection();
786     }
787 
788     /**
789      * Returns the sub selected item position started from zero.  An item can have
790      * multiple {@link ItemAlignmentFacet}s provided by {@link RecyclerView.ViewHolder}
791      * or {@link FacetProviderAdapter}.  Zero is returned when no {@link ItemAlignmentFacet}
792      * is defined.
793      * @hide
794      */
795     @RestrictTo(LIBRARY_GROUP)
getSelectedSubPosition()796     public int getSelectedSubPosition() {
797         return mLayoutManager.getSubSelection();
798     }
799 
800     /**
801      * Sets whether ItemAnimator should run when a child changes size or when adding
802      * or removing a child.
803      * @param animateChildLayout True to enable ItemAnimator, false to disable.
804      */
setAnimateChildLayout(boolean animateChildLayout)805     public void setAnimateChildLayout(boolean animateChildLayout) {
806         if (mAnimateChildLayout != animateChildLayout) {
807             mAnimateChildLayout = animateChildLayout;
808             if (!mAnimateChildLayout) {
809                 mSavedItemAnimator = getItemAnimator();
810                 super.setItemAnimator(null);
811             } else {
812                 super.setItemAnimator(mSavedItemAnimator);
813             }
814         }
815     }
816 
817     /**
818      * Returns true if an animation will run when a child changes size or when
819      * adding or removing a child.
820      * @return True if ItemAnimator is enabled, false otherwise.
821      */
isChildLayoutAnimated()822     public boolean isChildLayoutAnimated() {
823         return mAnimateChildLayout;
824     }
825 
826     /**
827      * Sets the gravity used for child view positioning. Defaults to
828      * GRAVITY_TOP|GRAVITY_START.
829      *
830      * @param gravity See {@link android.view.Gravity}
831      */
setGravity(int gravity)832     public void setGravity(int gravity) {
833         mLayoutManager.setGravity(gravity);
834         requestLayout();
835     }
836 
837     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)838     public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
839         return mLayoutManager.gridOnRequestFocusInDescendants(this, direction,
840                 previouslyFocusedRect);
841     }
842 
843     /**
844      * Returns the x/y offsets to final position from current position if the view
845      * is selected.
846      *
847      * @param view The view to get offsets.
848      * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of Y.
849      */
getViewSelectedOffsets(View view, int[] offsets)850     public void getViewSelectedOffsets(View view, int[] offsets) {
851         mLayoutManager.getViewSelectedOffsets(view, offsets);
852     }
853 
854     @Override
getChildDrawingOrder(int childCount, int i)855     public int getChildDrawingOrder(int childCount, int i) {
856         return mLayoutManager.getChildDrawingOrder(this, childCount, i);
857     }
858 
isChildrenDrawingOrderEnabledInternal()859     final boolean isChildrenDrawingOrderEnabledInternal() {
860         return isChildrenDrawingOrderEnabled();
861     }
862 
863     @Override
focusSearch(int direction)864     public View focusSearch(int direction) {
865         if (isFocused()) {
866             // focusSearch(int) is called when GridView itself is focused.
867             // Calling focusSearch(view, int) to get next sibling of current selected child.
868             View view = mLayoutManager.findViewByPosition(mLayoutManager.getSelection());
869             if (view != null) {
870                 return focusSearch(view, direction);
871             }
872         }
873         // otherwise, go to mParent to perform focusSearch
874         return super.focusSearch(direction);
875     }
876 
877     @Override
onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)878     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
879         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
880         mLayoutManager.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
881     }
882 
883     /**
884      * Disables or enables focus search.
885      * @param disabled True to disable focus search, false to enable.
886      */
setFocusSearchDisabled(boolean disabled)887     public final void setFocusSearchDisabled(boolean disabled) {
888         // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
889         // re-gain focus after a BACK key pressed, so block children focus during transition.
890         setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
891         mLayoutManager.setFocusSearchDisabled(disabled);
892     }
893 
894     /**
895      * Returns true if focus search is disabled.
896      * @return True if focus search is disabled.
897      */
isFocusSearchDisabled()898     public final boolean isFocusSearchDisabled() {
899         return mLayoutManager.isFocusSearchDisabled();
900     }
901 
902     /**
903      * Enables or disables layout.  All children will be removed when layout is
904      * disabled.
905      * @param layoutEnabled True to enable layout, false otherwise.
906      */
setLayoutEnabled(boolean layoutEnabled)907     public void setLayoutEnabled(boolean layoutEnabled) {
908         mLayoutManager.setLayoutEnabled(layoutEnabled);
909     }
910 
911     /**
912      * Changes and overrides children's visibility.
913      * @param visibility See {@link View#getVisibility()}.
914      */
setChildrenVisibility(int visibility)915     public void setChildrenVisibility(int visibility) {
916         mLayoutManager.setChildrenVisibility(visibility);
917     }
918 
919     /**
920      * Enables or disables pruning of children.  Disable is useful during transition.
921      * @param pruneChild True to prune children out side visible area, false to enable.
922      */
setPruneChild(boolean pruneChild)923     public void setPruneChild(boolean pruneChild) {
924         mLayoutManager.setPruneChild(pruneChild);
925     }
926 
927     /**
928      * Enables or disables scrolling.  Disable is useful during transition.
929      * @param scrollEnabled True to enable scroll, false to disable.
930      */
setScrollEnabled(boolean scrollEnabled)931     public void setScrollEnabled(boolean scrollEnabled) {
932         mLayoutManager.setScrollEnabled(scrollEnabled);
933     }
934 
935     /**
936      * Returns true if scrolling is enabled, false otherwise.
937      * @return True if scrolling is enabled, false otherwise.
938      */
isScrollEnabled()939     public boolean isScrollEnabled() {
940         return mLayoutManager.isScrollEnabled();
941     }
942 
943     /**
944      * Returns true if the view at the given position has a same row sibling
945      * in front of it.  This will return true if first item view is not created.
946      *
947      * @param position Position in adapter.
948      * @return True if the view at the given position has a same row sibling in front of it.
949      */
hasPreviousViewInSameRow(int position)950     public boolean hasPreviousViewInSameRow(int position) {
951         return mLayoutManager.hasPreviousViewInSameRow(position);
952     }
953 
954     /**
955      * Enables or disables the default "focus draw at last" order rule. Default is enabled.
956      * @param enabled True to draw the selected child at last, false otherwise.
957      */
setFocusDrawingOrderEnabled(boolean enabled)958     public void setFocusDrawingOrderEnabled(boolean enabled) {
959         super.setChildrenDrawingOrderEnabled(enabled);
960     }
961 
962     /**
963      * Returns true if draws selected child at last, false otherwise. Default is enabled.
964      * @return True if draws selected child at last, false otherwise.
965      */
isFocusDrawingOrderEnabled()966     public boolean isFocusDrawingOrderEnabled() {
967         return super.isChildrenDrawingOrderEnabled();
968     }
969 
970     /**
971      * Sets the touch intercept listener.
972      * @param listener The touch intercept listener.
973      */
setOnTouchInterceptListener(OnTouchInterceptListener listener)974     public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
975         mOnTouchInterceptListener = listener;
976     }
977 
978     /**
979      * Sets the generic motion intercept listener.
980      * @param listener The motion intercept listener.
981      */
setOnMotionInterceptListener(OnMotionInterceptListener listener)982     public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
983         mOnMotionInterceptListener = listener;
984     }
985 
986     /**
987      * Sets the key intercept listener.
988      * @param listener The key intercept listener.
989      */
setOnKeyInterceptListener(OnKeyInterceptListener listener)990     public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
991         mOnKeyInterceptListener = listener;
992     }
993 
994     /**
995      * Sets the unhandled key listener.
996      * @param listener The unhandled key intercept listener.
997      */
setOnUnhandledKeyListener(OnUnhandledKeyListener listener)998     public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
999         mOnUnhandledKeyListener = listener;
1000     }
1001 
1002     /**
1003      * Returns the unhandled key listener.
1004      * @return The unhandled key listener.
1005      */
getOnUnhandledKeyListener()1006     public OnUnhandledKeyListener getOnUnhandledKeyListener() {
1007         return mOnUnhandledKeyListener;
1008     }
1009 
1010     @Override
dispatchKeyEvent(KeyEvent event)1011     public boolean dispatchKeyEvent(KeyEvent event) {
1012         if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
1013             return true;
1014         }
1015         if (super.dispatchKeyEvent(event)) {
1016             return true;
1017         }
1018         return mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event);
1019     }
1020 
1021     @Override
dispatchTouchEvent(MotionEvent event)1022     public boolean dispatchTouchEvent(MotionEvent event) {
1023         if (mOnTouchInterceptListener != null) {
1024             if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
1025                 return true;
1026             }
1027         }
1028         return super.dispatchTouchEvent(event);
1029     }
1030 
1031     @Override
dispatchGenericFocusedEvent(MotionEvent event)1032     protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
1033         if (mOnMotionInterceptListener != null) {
1034             if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
1035                 return true;
1036             }
1037         }
1038         return super.dispatchGenericFocusedEvent(event);
1039     }
1040 
1041     /**
1042      * Returns the policy for saving children.
1043      *
1044      * @return policy, one of {@link #SAVE_NO_CHILD}
1045      * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
1046      */
getSaveChildrenPolicy()1047     public final int getSaveChildrenPolicy() {
1048         return mLayoutManager.mChildrenStates.getSavePolicy();
1049     }
1050 
1051     /**
1052      * Returns the limit used when when {@link #getSaveChildrenPolicy()} is
1053      *         {@link #SAVE_LIMITED_CHILD}
1054      */
getSaveChildrenLimitNumber()1055     public final int getSaveChildrenLimitNumber() {
1056         return mLayoutManager.mChildrenStates.getLimitNumber();
1057     }
1058 
1059     /**
1060      * Sets the policy for saving children.
1061      * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
1062      * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
1063      */
setSaveChildrenPolicy(int savePolicy)1064     public final void setSaveChildrenPolicy(int savePolicy) {
1065         mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
1066     }
1067 
1068     /**
1069      * Sets the limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
1070      */
setSaveChildrenLimitNumber(int limitNumber)1071     public final void setSaveChildrenLimitNumber(int limitNumber) {
1072         mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
1073     }
1074 
1075     @Override
hasOverlappingRendering()1076     public boolean hasOverlappingRendering() {
1077         return mHasOverlappingRendering;
1078     }
1079 
setHasOverlappingRendering(boolean hasOverlapping)1080     public void setHasOverlappingRendering(boolean hasOverlapping) {
1081         mHasOverlappingRendering = hasOverlapping;
1082     }
1083 
1084     /**
1085      * Notify layout manager that layout directionality has been updated
1086      */
1087     @Override
onRtlPropertiesChanged(int layoutDirection)1088     public void onRtlPropertiesChanged(int layoutDirection) {
1089         mLayoutManager.onRtlPropertiesChanged(layoutDirection);
1090     }
1091 
1092     @Override
setRecyclerListener(RecyclerView.RecyclerListener listener)1093     public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
1094         mChainedRecyclerListener = listener;
1095     }
1096 
1097     /**
1098      * Sets pixels of extra space for layout child in invisible area.
1099      *
1100      * @param extraLayoutSpace  Pixels of extra space for layout invisible child.
1101      *                          Must be bigger or equals to 0.
1102      * @hide
1103      */
1104     @RestrictTo(LIBRARY_GROUP)
setExtraLayoutSpace(int extraLayoutSpace)1105     public void setExtraLayoutSpace(int extraLayoutSpace) {
1106         mLayoutManager.setExtraLayoutSpace(extraLayoutSpace);
1107     }
1108 
1109     /**
1110      * Returns pixels of extra space for layout child in invisible area.
1111      *
1112      * @hide
1113      */
1114     @RestrictTo(LIBRARY_GROUP)
getExtraLayoutSpace()1115     public int getExtraLayoutSpace() {
1116         return mLayoutManager.getExtraLayoutSpace();
1117     }
1118 
1119     /**
1120      * Temporarily slide out child views to bottom (for VerticalGridView) or end
1121      * (for HorizontalGridView). Layout and scrolling will be suppressed until
1122      * {@link #animateIn()} is called.
1123      */
animateOut()1124     public void animateOut() {
1125         mLayoutManager.slideOut();
1126     }
1127 
1128     /**
1129      * Undo animateOut() and slide in child views.
1130      */
animateIn()1131     public void animateIn() {
1132         mLayoutManager.slideIn();
1133     }
1134 
1135     @Override
scrollToPosition(int position)1136     public void scrollToPosition(int position) {
1137         // dont abort the animateOut() animation, just record the position
1138         if (mLayoutManager.isSlidingChildViews()) {
1139             mLayoutManager.setSelectionWithSub(position, 0, 0);
1140             return;
1141         }
1142         super.scrollToPosition(position);
1143     }
1144 
1145     @Override
smoothScrollToPosition(int position)1146     public void smoothScrollToPosition(int position) {
1147         // dont abort the animateOut() animation, just record the position
1148         if (mLayoutManager.isSlidingChildViews()) {
1149             mLayoutManager.setSelectionWithSub(position, 0, 0);
1150             return;
1151         }
1152         super.smoothScrollToPosition(position);
1153     }
1154 
1155     /**
1156      * Sets the number of items to prefetch in
1157      * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
1158      * which defines how many inner items should be prefetched when this GridView is nested inside
1159      * another RecyclerView.
1160      *
1161      * <p>Set this value to the number of items this inner GridView will display when it is
1162      * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items
1163      * so they are ready, avoiding jank as the inner GridView is scrolled into the viewport.</p>
1164      *
1165      * <p>For example, take a VerticalGridView of scrolling HorizontalGridViews. The rows always
1166      * have 6 items visible in them (or 7 if not aligned). Passing <code>6</code> to this method
1167      * for each inner GridView will enable RecyclerView's prefetching feature to do create/bind work
1168      * for 6 views within a row early, before it is scrolled on screen, instead of just the default
1169      * 4.</p>
1170      *
1171      * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView
1172      * nested in another RecyclerView.</p>
1173      *
1174      * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of
1175      * views that will be visible in this view can incur unnecessary bind work, and an increase to
1176      * the number of Views created and in active use.</p>
1177      *
1178      * @param itemCount Number of items to prefetch
1179      *
1180      * @see #getInitialPrefetchItemCount()
1181      * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
1182      * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)
1183      */
setInitialPrefetchItemCount(int itemCount)1184     public void setInitialPrefetchItemCount(int itemCount) {
1185         mInitialPrefetchItemCount = itemCount;
1186     }
1187 
1188     /**
1189      * Gets the number of items to prefetch in
1190      * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
1191      * which defines how many inner items should be prefetched when this GridView is nested inside
1192      * another RecyclerView.
1193      *
1194      * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
1195      * @see #setInitialPrefetchItemCount(int)
1196      * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)
1197      *
1198      * @return number of items to prefetch.
1199      */
getInitialPrefetchItemCount()1200     public int getInitialPrefetchItemCount() {
1201         return mInitialPrefetchItemCount;
1202     }
1203 }
1204