public abstract class

BaseGridView

extends RecyclerView

 java.lang.Object

↳ViewGroup

androidx.recyclerview.widget.RecyclerView

↳androidx.leanback.widget.BaseGridView

Subclasses:

HorizontalGridView, VerticalGridView

Gradle dependencies

compile group: 'androidx.leanback', name: 'leanback-grid', version: '1.0.0-alpha03'

  • groupId: androidx.leanback
  • artifactId: leanback-grid
  • version: 1.0.0-alpha03

Artifact androidx.leanback:leanback-grid:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Androidx class mapping:

androidx.leanback.widget.BaseGridView android.support.v17.leanback.widget.BaseGridView

Overview

An abstract base class for vertically and horizontally scrolling lists. The items come from the associated with this view. Do not directly use this class, use VerticalGridView and HorizontalGridView. The class is not intended to be subclassed other than VerticalGridView and HorizontalGridView.

Summary

Fields
public static final intFOCUS_SCROLL_ALIGNED

Always keep focused item at a aligned position.

public static final intFOCUS_SCROLL_ITEM

Scroll to make the focused item inside client area.

public static final intFOCUS_SCROLL_PAGE

Scroll a page of items when focusing to item outside the client area.

public static final floatITEM_ALIGN_OFFSET_PERCENT_DISABLED

Value indicates that percent is not used.

public static final intSAVE_ALL_CHILD

Save on screen views plus save off screen child views without any limitation.

public static final intSAVE_LIMITED_CHILD

Save on screen views plus save off screen child views states up to BaseGridView.getSaveChildrenLimitNumber().

public static final intSAVE_NO_CHILD

Dont save states of any child views.

public static final intSAVE_ON_SCREEN_CHILD

Only save on screen child views, the states are lost when they become off screen.

public static final intWINDOW_ALIGN_BOTH_EDGE

The first item and last item are aligned with the two edges of the viewport.

public static final intWINDOW_ALIGN_HIGH_EDGE

The last item is aligned with the high edge of the viewport when navigating to the end of list.

public static final intWINDOW_ALIGN_LOW_EDGE

The first item is aligned with the low edge of the viewport.

public static final intWINDOW_ALIGN_NO_EDGE

The focused item always stays in a key line location.

public static final floatWINDOW_ALIGN_OFFSET_PERCENT_DISABLED

Value indicates that percent is not used.

from RecyclerViewHORIZONTAL, INVALID_TYPE, NO_ID, NO_POSITION, SCROLL_STATE_DRAGGING, SCROLL_STATE_IDLE, SCROLL_STATE_SETTLING, TOUCH_SLOP_DEFAULT, TOUCH_SLOP_PAGING, UNDEFINED_DURATION, VERTICAL
Methods
public voidaddOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected.

public final voidaddOnLayoutCompletedListener(BaseGridView.OnLayoutCompletedListener listener)

Registers a callback to be invoked when the BaseGridView completes a full layout calculation.

public voidanimateIn()

Undo animateOut() and slide in child views.

public voidanimateOut()

Temporarily slide out child views to bottom (for VerticalGridView) or end (for HorizontalGridView).

protected booleandispatchGenericFocusedEvent(MotionEvent event)

public booleandispatchKeyEvent(KeyEvent event)

public booleandispatchTouchEvent(MotionEvent event)

public ViewfocusSearch(int direction)

public intgetChildDrawingOrder(int childCount, int i)

public intgetExtraLayoutSpace()

Returns pixels of extra space for layout child in invisible area.

public intgetFocusScrollStrategy()

Returns the strategy used to scroll in response to item focus changing.

public intgetHorizontalMargin()

Returns the spacing in pixels between two child items horizontally.

public intgetHorizontalSpacing()

Returns the horizontal spacing in pixels between two child items.

public intgetInitialPrefetchItemCount()

Gets the number of items to prefetch in , which defines how many inner items should be prefetched when this GridView is nested inside another RecyclerView.

public intgetItemAlignmentOffset()

Returns number of pixels to the end of low edge.

public floatgetItemAlignmentOffsetPercent()

Returns the offset percent for item alignment in addition to BaseGridView.getItemAlignmentOffset().

public intgetItemAlignmentViewId()

Returns the id of the view to align with, or android.view.View for the root .

public BaseGridView.OnUnhandledKeyListenergetOnUnhandledKeyListener()

Returns the unhandled key listener.

public final intgetSaveChildrenLimitNumber()

Returns the limit used when when BaseGridView.getSaveChildrenPolicy() is BaseGridView.SAVE_LIMITED_CHILD

public final intgetSaveChildrenPolicy()

Returns the policy for saving children.

public intgetSelectedPosition()

Returns the adapter position of selected item.

public intgetSelectedSubPosition()

Returns the sub selected item position started from zero.

public BaseGridView.SmoothScrollByBehaviorgetSmoothScrollByBehavior()

Returns custom behavior for smoothScrollBy().

public final intgetSmoothScrollMaxPendingMoves()

When holding DPAD, DPAD events are generated faster than the grid view can scroll.

public final floatgetSmoothScrollSpeedFactor()

public intgetVerticalMargin()

Returns the spacing in pixels between two child items vertically.

public intgetVerticalSpacing()

Returns the vertical spacing in pixels between two child items.

public voidgetViewSelectedOffsets(View view, int[] offsets[])

Returns the x/y offsets to final position from current position if the view is selected.

public intgetWindowAlignment()

Returns the method for focused item alignment in the view.

public intgetWindowAlignmentOffset()

Returns the offset in pixels for window alignment key line.

public floatgetWindowAlignmentOffsetPercent()

Returns the offset percent for window alignment key line in addition to BaseGridView.getWindowAlignmentOffset().

public booleanhasOverlappingRendering()

public booleanhasPreviousViewInSameRow(int position)

Returns true if the view at the given position has a same row sibling in front of it.

public booleanisChildLayoutAnimated()

Returns true if an animation will run when a child changes size or when adding or removing a child.

public booleanisFocusDrawingOrderEnabled()

Returns true if draws selected child at last, false otherwise.

public final booleanisFocusSearchDisabled()

Returns true if focus search is disabled.

public booleanisItemAlignmentOffsetWithPadding()

Returns true if applies padding to item alignment when BaseGridView.getItemAlignmentOffsetPercent() is 0 or 100; returns false otherwise.

public booleanisScrollEnabled()

Returns true if scrolling is enabled, false otherwise.

public booleanisWindowAlignmentPreferKeyLineOverHighEdge()

Returns whether prefer key line over high edge when BaseGridView.WINDOW_ALIGN_HIGH_EDGE is used.

public booleanisWindowAlignmentPreferKeyLineOverLowEdge()

Returns whether prefer key line over low edge when BaseGridView.WINDOW_ALIGN_LOW_EDGE is used.

protected voidonFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)

public booleanonRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)

public voidonRtlPropertiesChanged(int layoutDirection)

Notify layout manager that layout directionality has been updated

public voidremoveOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Remove the callback invoked when an item in BaseGridView has been selected.

public final voidremoveOnLayoutCompletedListener(BaseGridView.OnLayoutCompletedListener listener)

Removes a callback to be invoked when the BaseGridView completes a full layout calculation.

public voidremoveView(View view)

public voidremoveViewAt(int index)

public voidscrollToPosition(int position)

Convenience method to scroll to a certain position.

public voidsetAnimateChildLayout(boolean animateChildLayout)

Sets whether ItemAnimator should run when a child changes size or when adding or removing a child.

public voidsetChildrenVisibility(int visibility)

Changes and overrides children's visibility.

public voidsetExtraLayoutSpace(int extraLayoutSpace)

Sets pixels of extra space for layout child in invisible area.

public voidsetFocusDrawingOrderEnabled(boolean enabled)

Enables or disables the default "focus draw at last" order rule.

public voidsetFocusScrollStrategy(int scrollStrategy)

Sets the strategy used to scroll in response to item focus changing:

public final voidsetFocusSearchDisabled(boolean disabled)

Disables or enables focus search.

public voidsetGravity(int gravity)

Sets the gravity used for child view positioning.

public voidsetHasOverlappingRendering(boolean hasOverlapping)

public voidsetHorizontalMargin(int margin)

Sets the spacing in pixels between two child items horizontally.

public voidsetHorizontalSpacing(int spacing)

Sets the horizontal spacing in pixels between two child items.

public voidsetInitialPrefetchItemCount(int itemCount)

Sets the number of items to prefetch in , which defines how many inner items should be prefetched when this GridView is nested inside another RecyclerView.

public voidsetItemAlignmentOffset(int offset)

Sets number of pixels to the end of low edge.

public voidsetItemAlignmentOffsetPercent(float offsetPercent)

Sets the offset percent for item alignment in addition to BaseGridView.getItemAlignmentOffset().

public voidsetItemAlignmentOffsetWithPadding(boolean withPadding)

Sets whether applies padding to item alignment when BaseGridView.getItemAlignmentOffsetPercent() is 0 or 100.

public voidsetItemAlignmentViewId(int viewId)

Sets the id of the view to align with.

public voidsetItemMargin(int margin)

Sets the spacing in pixels between two child items.

public voidsetItemSpacing(int spacing)

Sets the vertical and horizontal spacing in pixels between two child items.

public voidsetLayoutEnabled(boolean layoutEnabled)

Enables or disables layout.

public voidsetLayoutManager(RecyclerView.LayoutManager layout)

Set the RecyclerView.LayoutManager that this RecyclerView will use.

public voidsetOnChildLaidOutListener(OnChildLaidOutListener listener)

Registers a callback to be invoked when an item in BaseGridView has been laid out.

public voidsetOnChildSelectedListener(OnChildSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected.

public voidsetOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected.

public voidsetOnKeyInterceptListener(BaseGridView.OnKeyInterceptListener listener)

Sets the key intercept listener.

public voidsetOnMotionInterceptListener(BaseGridView.OnMotionInterceptListener listener)

Sets the generic motion intercept listener.

public voidsetOnTouchInterceptListener(BaseGridView.OnTouchInterceptListener listener)

Sets the touch intercept listener.

public voidsetOnUnhandledKeyListener(BaseGridView.OnUnhandledKeyListener listener)

Sets the unhandled key listener.

public voidsetPruneChild(boolean pruneChild)

Enables or disables pruning of children.

public final voidsetSaveChildrenLimitNumber(int limitNumber)

Sets the limit number when BaseGridView.getSaveChildrenPolicy() is BaseGridView.SAVE_LIMITED_CHILD.

public final voidsetSaveChildrenPolicy(int savePolicy)

Sets the policy for saving children.

public voidsetScrollEnabled(boolean scrollEnabled)

Enables or disables scrolling.

public voidsetSelectedPosition(int position)

Changes the selected item immediately without animation.

public voidsetSelectedPosition(int position, int scrollExtra)

Changes the selected item immediately without animation, scrollExtra is applied in primary scroll direction.

public voidsetSelectedPosition(int position, ViewHolderTask task)

Perform a task on ViewHolder at given position after scroll to it.

public voidsetSelectedPositionSmooth(int position)

Changes the selected item and run an animation to scroll to the target position.

public voidsetSelectedPositionSmooth(int position, ViewHolderTask task)

Perform a task on ViewHolder at given position after smooth scrolling to it.

public voidsetSelectedPositionSmoothWithSub(int position, int subposition)

Changes the selected item and/or subposition, runs an animation to scroll to the target position.

public voidsetSelectedPositionWithSub(int position, int subposition)

Changes the selected item and/or subposition immediately without animation.

public voidsetSelectedPositionWithSub(int position, int subposition, int scrollExtra)

Changes the selected item and/or subposition immediately without animation, scrollExtra is applied in primary scroll direction.

public final voidsetSmoothScrollByBehavior(BaseGridView.SmoothScrollByBehavior behavior)

Set custom behavior for smoothScrollBy().

public final voidsetSmoothScrollMaxPendingMoves(int maxPendingMoves)

When holding DPAD, DPAD events are generated faster than the grid view can scroll.

public final voidsetSmoothScrollSpeedFactor(float smoothScrollSpeedFactor)

Set factor of how slow the smoothScroller should run.

public voidsetVerticalMargin(int margin)

Sets the spacing in pixels between two child items vertically.

public voidsetVerticalSpacing(int spacing)

Sets the vertical spacing in pixels between two child items.

public voidsetWindowAlignment(int windowAlignment)

Sets the method for focused item alignment in the view.

public voidsetWindowAlignmentOffset(int offset)

Sets the offset in pixels for window alignment key line.

public voidsetWindowAlignmentOffsetPercent(float offsetPercent)

Sets the offset percent for window alignment key line in addition to BaseGridView.getWindowAlignmentOffset().

public voidsetWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge)

Returns whether prefer key line over high edge when BaseGridView.WINDOW_ALIGN_HIGH_EDGE is used.

public voidsetWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge)

Sets whether prefer key line over low edge when BaseGridView.WINDOW_ALIGN_LOW_EDGE is used.

public voidsmoothScrollBy(int dx, int dy)

Animate a scroll by the given amount of pixels along either axis.

public voidsmoothScrollBy(int dx, int dy, Interpolator interpolator)

Animate a scroll by the given amount of pixels along either axis.

public voidsmoothScrollToPosition(int position)

Starts a smooth scroll to an adapter position.

from RecyclerViewaddFocusables, addItemDecoration, addItemDecoration, addOnChildAttachStateChangeListener, addOnItemTouchListener, addOnScrollListener, addRecyclerListener, checkLayoutParams, clearOnChildAttachStateChangeListeners, clearOnScrollListeners, computeHorizontalScrollExtent, computeHorizontalScrollOffset, computeHorizontalScrollRange, computeVerticalScrollExtent, computeVerticalScrollOffset, computeVerticalScrollRange, dispatchNestedFling, dispatchNestedPreFling, dispatchNestedPreScroll, dispatchNestedPreScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchPopulateAccessibilityEvent, dispatchRestoreInstanceState, dispatchSaveInstanceState, draw, drawChild, findChildViewUnder, findContainingItemView, findContainingViewHolder, findViewHolderForAdapterPosition, findViewHolderForItemId, findViewHolderForLayoutPosition, findViewHolderForPosition, fling, focusSearch, generateDefaultLayoutParams, generateLayoutParams, generateLayoutParams, getAccessibilityClassName, getAdapter, getBaseline, getChildAdapterPosition, getChildItemId, getChildLayoutPosition, getChildPosition, getChildViewHolder, getClipToPadding, getCompatAccessibilityDelegate, getDecoratedBoundsWithMargins, getEdgeEffectFactory, getItemAnimator, getItemDecorationAt, getItemDecorationCount, getLayoutManager, getMaxFlingVelocity, getMinFlingVelocity, getOnFlingListener, getPreserveFocusAfterLayout, getRecycledViewPool, getScrollState, hasFixedSize, hasNestedScrollingParent, hasNestedScrollingParent, hasPendingAdapterUpdates, invalidateItemDecorations, isAnimating, isAttachedToWindow, isComputingLayout, isLayoutFrozen, isLayoutSuppressed, isNestedScrollingEnabled, nestedScrollBy, offsetChildrenHorizontal, offsetChildrenVertical, onAttachedToWindow, onChildAttachedToWindow, onChildDetachedFromWindow, onDetachedFromWindow, onDraw, onGenericMotionEvent, onInterceptTouchEvent, onLayout, onMeasure, onRestoreInstanceState, onSaveInstanceState, onScrolled, onScrollStateChanged, onSizeChanged, onTouchEvent, removeDetachedView, removeItemDecoration, removeItemDecorationAt, removeOnChildAttachStateChangeListener, removeOnItemTouchListener, removeOnScrollListener, removeRecyclerListener, requestChildFocus, requestChildRectangleOnScreen, requestDisallowInterceptTouchEvent, requestLayout, scrollBy, scrollTo, sendAccessibilityEventUnchecked, setAccessibilityDelegateCompat, setAdapter, setChildDrawingOrderCallback, setClipToPadding, setDebugAssertionsEnabled, setEdgeEffectFactory, setHasFixedSize, setItemAnimator, setItemViewCacheSize, setLayoutFrozen, setLayoutTransition, setNestedScrollingEnabled, setOnFlingListener, setOnScrollListener, setPreserveFocusAfterLayout, setRecycledViewPool, setRecyclerListener, setScrollingTouchSlop, setVerboseLoggingEnabled, setViewCacheExtension, smoothScrollBy, startNestedScroll, startNestedScroll, stopNestedScroll, stopNestedScroll, stopScroll, suppressLayout, swapAdapter
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int FOCUS_SCROLL_ALIGNED

Always keep focused item at a aligned position. Developer can use WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned. In this mode, the last focused position will be remembered and restored when focus is back to the view.

public static final int FOCUS_SCROLL_ITEM

Scroll to make the focused item inside client area.

public static final int FOCUS_SCROLL_PAGE

Scroll a page of items when focusing to item outside the client area. The page size matches the client area size of RecyclerView.

public static final int WINDOW_ALIGN_LOW_EDGE

The first item is aligned with the low edge of the viewport. When navigating away from the first item, the focus item is aligned to a key line location.

For HorizontalGridView, low edge refers to getPaddingLeft() when RTL is false or getWidth() - getPaddingRight() when RTL is true. For VerticalGridView, low edge refers to getPaddingTop().

The key line location is calculated by "windowAlignOffset" and "windowAlignOffsetPercent"; if neither of these two is defined, the default value is 1/2 of the size.

Note if there are very few items between low edge and key line, use BaseGridView.setWindowAlignmentPreferKeyLineOverLowEdge(boolean) to control whether you prefer to align the items to key line or low edge. Default is preferring low edge.

public static final int WINDOW_ALIGN_HIGH_EDGE

The last item is aligned with the high edge of the viewport when navigating to the end of list. When navigating away from the end, the focus item is aligned to a key line location.

For HorizontalGridView, high edge refers to getWidth() - getPaddingRight() when RTL is false or getPaddingLeft() when RTL is true. For VerticalGridView, high edge refers to getHeight() - getPaddingBottom().

The key line location is calculated by "windowAlignOffset" and "windowAlignOffsetPercent"; if neither of these two is defined, the default value is 1/2 of the size.

Note if there are very few items between high edge and key line, use BaseGridView.setWindowAlignmentPreferKeyLineOverHighEdge(boolean) to control whether you prefer to align the items to key line or high edge. Default is preferring key line.

public static final int WINDOW_ALIGN_BOTH_EDGE

The first item and last item are aligned with the two edges of the viewport. When navigating in the middle of list, the focus maintains a key line location.

The key line location is calculated by "windowAlignOffset" and "windowAlignOffsetPercent"; if neither of these two is defined, the default value is 1/2 of the size.

public static final int WINDOW_ALIGN_NO_EDGE

The focused item always stays in a key line location.

The key line location is calculated by "windowAlignOffset" and "windowAlignOffsetPercent"; if neither of these two is defined, the default value is 1/2 of the size.

public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED

Value indicates that percent is not used.

public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED

Value indicates that percent is not used.

public static final int SAVE_NO_CHILD

Dont save states of any child views.

public static final int SAVE_ON_SCREEN_CHILD

Only save on screen child views, the states are lost when they become off screen.

public static final int SAVE_LIMITED_CHILD

Save on screen views plus save off screen child views states up to BaseGridView.getSaveChildrenLimitNumber().

public static final int SAVE_ALL_CHILD

Save on screen views plus save off screen child views without any limitation. This might cause out of memory, only use it when you are dealing with limited data.

Methods

public void setFocusScrollStrategy(int scrollStrategy)

Sets the strategy used to scroll in response to item focus changing:

public int getFocusScrollStrategy()

Returns the strategy used to scroll in response to item focus changing.

public void setWindowAlignment(int windowAlignment)

Sets the method for focused item alignment in the view.

Parameters:

windowAlignment: BaseGridView.WINDOW_ALIGN_BOTH_EDGE, BaseGridView.WINDOW_ALIGN_LOW_EDGE, BaseGridView.WINDOW_ALIGN_HIGH_EDGE or BaseGridView.WINDOW_ALIGN_NO_EDGE.

public int getWindowAlignment()

Returns the method for focused item alignment in the view.

Returns:

BaseGridView.WINDOW_ALIGN_BOTH_EDGE, BaseGridView.WINDOW_ALIGN_LOW_EDGE, BaseGridView.WINDOW_ALIGN_HIGH_EDGE or BaseGridView.WINDOW_ALIGN_NO_EDGE.

public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge)

Sets whether prefer key line over low edge when BaseGridView.WINDOW_ALIGN_LOW_EDGE is used. When true, if there are very few items between low edge and key line, align items to key line instead of align items to low edge. Default value is false (aka prefer align to low edge).

Parameters:

preferKeyLineOverLowEdge: True to prefer key line over low edge, false otherwise.

public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge)

Returns whether prefer key line over high edge when BaseGridView.WINDOW_ALIGN_HIGH_EDGE is used. When true, if there are very few items between high edge and key line, align items to key line instead of align items to high edge. Default value is true (aka prefer align to key line).

Parameters:

preferKeyLineOverHighEdge: True to prefer key line over high edge, false otherwise.

public boolean isWindowAlignmentPreferKeyLineOverLowEdge()

Returns whether prefer key line over low edge when BaseGridView.WINDOW_ALIGN_LOW_EDGE is used. When true, if there are very few items between low edge and key line, align items to key line instead of align items to low edge. Default value is false (aka prefer align to low edge).

Returns:

True to prefer key line over low edge, false otherwise.

public boolean isWindowAlignmentPreferKeyLineOverHighEdge()

Returns whether prefer key line over high edge when BaseGridView.WINDOW_ALIGN_HIGH_EDGE is used. When true, if there are very few items between high edge and key line, align items to key line instead of align items to high edge. Default value is true (aka prefer align to key line).

Returns:

True to prefer key line over high edge, false otherwise.

public void setWindowAlignmentOffset(int offset)

Sets the offset in pixels for window alignment key line.

Parameters:

offset: The number of pixels to offset. If the offset is positive, it is distance from low edge (see BaseGridView.WINDOW_ALIGN_LOW_EDGE); if the offset is negative, the absolute value is distance from high edge (see BaseGridView.WINDOW_ALIGN_HIGH_EDGE). Default value is 0.

public int getWindowAlignmentOffset()

Returns the offset in pixels for window alignment key line.

Returns:

The number of pixels to offset. If the offset is positive, it is distance from low edge (see BaseGridView.WINDOW_ALIGN_LOW_EDGE); if the offset is negative, the absolute value is distance from high edge (see BaseGridView.WINDOW_ALIGN_HIGH_EDGE). Default value is 0.

public void setWindowAlignmentOffsetPercent(float offsetPercent)

Sets the offset percent for window alignment key line in addition to BaseGridView.getWindowAlignmentOffset().

Parameters:

offsetPercent: Percentage to offset. E.g., 40 means 40% of the width from low edge. Use BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED to disable. Default value is 50.

public float getWindowAlignmentOffsetPercent()

Returns the offset percent for window alignment key line in addition to BaseGridView.getWindowAlignmentOffset().

Returns:

Percentage to offset. E.g., 40 means 40% of the width from the low edge, or BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED if disabled. Default value is 50.

public void setItemAlignmentOffset(int offset)

Sets number of pixels to the end of low edge. Supports right to left layout direction. Item alignment settings are ignored for the child if ItemAlignmentFacet is provided by or FacetProviderAdapter.

Parameters:

offset: In left to right or vertical case, it's the offset added to left/top edge. In right to left case, it's the offset subtracted from right edge.

public int getItemAlignmentOffset()

Returns number of pixels to the end of low edge. Supports right to left layout direction. In left to right or vertical case, it's the offset added to left/top edge. In right to left case, it's the offset subtracted from right edge. Item alignment settings are ignored for the child if ItemAlignmentFacet is provided by or FacetProviderAdapter.

Returns:

The number of pixels to the end of low edge.

public void setItemAlignmentOffsetWithPadding(boolean withPadding)

Sets whether applies padding to item alignment when BaseGridView.getItemAlignmentOffsetPercent() is 0 or 100.

When true: Applies start/top padding if BaseGridView.getItemAlignmentOffsetPercent() is 0. Applies end/bottom padding if BaseGridView.getItemAlignmentOffsetPercent() is 100. Does not apply padding if BaseGridView.getItemAlignmentOffsetPercent() is neither 0 nor 100.

When false: does not apply padding

public boolean isItemAlignmentOffsetWithPadding()

Returns true if applies padding to item alignment when BaseGridView.getItemAlignmentOffsetPercent() is 0 or 100; returns false otherwise.

When true: Applies start/top padding when BaseGridView.getItemAlignmentOffsetPercent() is 0. Applies end/bottom padding when BaseGridView.getItemAlignmentOffsetPercent() is 100. Does not apply padding if BaseGridView.getItemAlignmentOffsetPercent() is neither 0 nor 100.

When false: does not apply padding

public void setItemAlignmentOffsetPercent(float offsetPercent)

Sets the offset percent for item alignment in addition to BaseGridView.getItemAlignmentOffset(). Item alignment settings are ignored for the child if ItemAlignmentFacet is provided by or FacetProviderAdapter.

Parameters:

offsetPercent: Percentage to offset. E.g., 40 means 40% of the width from the low edge. Use BaseGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED to disable.

public float getItemAlignmentOffsetPercent()

Returns the offset percent for item alignment in addition to BaseGridView.getItemAlignmentOffset().

Returns:

Percentage to offset. E.g., 40 means 40% of the width from the low edge, or BaseGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED if disabled. Default value is 50.

public void setItemAlignmentViewId(int viewId)

Sets the id of the view to align with. Use android.view.View (default) for the root . Item alignment settings on BaseGridView are if ItemAlignmentFacet is provided by or FacetProviderAdapter.

public int getItemAlignmentViewId()

Returns the id of the view to align with, or android.view.View for the root .

Returns:

The id of the view to align with, or android.view.View for the root .

public void setItemMargin(int margin)

Deprecated: use BaseGridView.setItemSpacing(int)

Sets the spacing in pixels between two child items.

public void setItemSpacing(int spacing)

Sets the vertical and horizontal spacing in pixels between two child items.

Parameters:

spacing: Vertical and horizontal spacing in pixels between two child items.

public void setVerticalMargin(int margin)

Deprecated: Use BaseGridView.setVerticalSpacing(int)

Sets the spacing in pixels between two child items vertically.

public int getVerticalMargin()

Deprecated: Use BaseGridView.getVerticalSpacing()

Returns the spacing in pixels between two child items vertically.

public void setHorizontalMargin(int margin)

Deprecated: Use BaseGridView.setHorizontalSpacing(int)

Sets the spacing in pixels between two child items horizontally.

public int getHorizontalMargin()

Deprecated: Use BaseGridView.getHorizontalSpacing()

Returns the spacing in pixels between two child items horizontally.

public void setVerticalSpacing(int spacing)

Sets the vertical spacing in pixels between two child items.

Parameters:

spacing: Vertical spacing between two child items.

public int getVerticalSpacing()

Returns the vertical spacing in pixels between two child items.

Returns:

The vertical spacing in pixels between two child items.

public void setHorizontalSpacing(int spacing)

Sets the horizontal spacing in pixels between two child items.

Parameters:

spacing: Horizontal spacing in pixels between two child items.

public int getHorizontalSpacing()

Returns the horizontal spacing in pixels between two child items.

Returns:

The Horizontal spacing in pixels between two child items.

public void setOnChildLaidOutListener(OnChildLaidOutListener listener)

Registers a callback to be invoked when an item in BaseGridView has been laid out.

Parameters:

listener: The listener to be invoked.

public void setOnChildSelectedListener(OnChildSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected. Note that the listener may be invoked when there is a layout pending on the view, affording the listener an opportunity to adjust the upcoming layout based on the selection state.

Parameters:

listener: The listener to be invoked.

public final void addOnLayoutCompletedListener(BaseGridView.OnLayoutCompletedListener listener)

Registers a callback to be invoked when the BaseGridView completes a full layout calculation.

Parameters:

listener: The listener to be invoked.

public final void removeOnLayoutCompletedListener(BaseGridView.OnLayoutCompletedListener listener)

Removes a callback to be invoked when the BaseGridView completes a full layout calculation.

Parameters:

listener: The listener to be invoked.

public void setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected. Note that the listener may be invoked when there is a layout pending on the view, affording the listener an opportunity to adjust the upcoming layout based on the selection state. This method will clear all existing listeners added by BaseGridView.addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener).

Parameters:

listener: The listener to be invoked.

public void addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Registers a callback to be invoked when an item in BaseGridView has been selected. Note that the listener may be invoked when there is a layout pending on the view, affording the listener an opportunity to adjust the upcoming layout based on the selection state.

Parameters:

listener: The listener to be invoked.

public void removeOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)

Remove the callback invoked when an item in BaseGridView has been selected.

Parameters:

listener: The listener to be removed.

public void setSelectedPosition(int position)

Changes the selected item immediately without animation.

public void setSelectedPositionWithSub(int position, int subposition)

Changes the selected item and/or subposition immediately without animation.

public void setSelectedPosition(int position, int scrollExtra)

Changes the selected item immediately without animation, scrollExtra is applied in primary scroll direction. The scrollExtra will be kept until another BaseGridView.setSelectedPosition(int) or BaseGridView.setSelectedPositionSmooth(int) call.

public void setSelectedPositionWithSub(int position, int subposition, int scrollExtra)

Changes the selected item and/or subposition immediately without animation, scrollExtra is applied in primary scroll direction. The scrollExtra will be kept until another BaseGridView.setSelectedPosition(int) or BaseGridView.setSelectedPositionSmooth(int) call.

public void setSelectedPositionSmooth(int position)

Changes the selected item and run an animation to scroll to the target position.

Parameters:

position: Adapter position of the item to select.

public void setSelectedPositionSmoothWithSub(int position, int subposition)

Changes the selected item and/or subposition, runs an animation to scroll to the target position.

public void setSelectedPositionSmooth(int position, ViewHolderTask task)

Perform a task on ViewHolder at given position after smooth scrolling to it.

Parameters:

position: Position of item in adapter.
task: Task to executed on the ViewHolder at a given position.

public void setSelectedPosition(int position, ViewHolderTask task)

Perform a task on ViewHolder at given position after scroll to it.

Parameters:

position: Position of item in adapter.
task: Task to executed on the ViewHolder at a given position.

public int getSelectedPosition()

Returns the adapter position of selected item.

Returns:

The adapter position of selected item.

public int getSelectedSubPosition()

Returns the sub selected item position started from zero. An item can have multiple ItemAlignmentFacets provided by or FacetProviderAdapter. Zero is returned when no ItemAlignmentFacet is defined.

public void setAnimateChildLayout(boolean animateChildLayout)

Sets whether ItemAnimator should run when a child changes size or when adding or removing a child.

Parameters:

animateChildLayout: True to enable ItemAnimator, false to disable.

public boolean isChildLayoutAnimated()

Returns true if an animation will run when a child changes size or when adding or removing a child.

Returns:

True if ItemAnimator is enabled, false otherwise.

public void setGravity(int gravity)

Sets the gravity used for child view positioning. Defaults to GRAVITY_TOP|GRAVITY_START.

Parameters:

gravity: See

public void setLayoutManager(RecyclerView.LayoutManager layout)

Set the RecyclerView.LayoutManager that this RecyclerView will use.

In contrast to other adapter-backed views such as or , RecyclerView allows client code to provide custom layout arrangements for child views. These arrangements are controlled by the RecyclerView.LayoutManager. A LayoutManager must be provided for RecyclerView to function.

Several default strategies are provided for common uses such as lists and grids.

Parameters:

layout: LayoutManager to use

public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)

public void getViewSelectedOffsets(View view, int[] offsets[])

Returns the x/y offsets to final position from current position if the view is selected.

Parameters:

view: The view to get offsets.
offsets: offsets[0] holds offset of X, offsets[1] holds offset of Y.

public int getChildDrawingOrder(int childCount, int i)

public View focusSearch(int direction)

protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)

public final void setFocusSearchDisabled(boolean disabled)

Disables or enables focus search.

Parameters:

disabled: True to disable focus search, false to enable.

public final boolean isFocusSearchDisabled()

Returns true if focus search is disabled.

Returns:

True if focus search is disabled.

public void setLayoutEnabled(boolean layoutEnabled)

Enables or disables layout. All children will be removed when layout is disabled.

Parameters:

layoutEnabled: True to enable layout, false otherwise.

public void setChildrenVisibility(int visibility)

Changes and overrides children's visibility.

Parameters:

visibility: See View.

public void setPruneChild(boolean pruneChild)

Enables or disables pruning of children. Disable is useful during transition.

Parameters:

pruneChild: True to prune children out side visible area, false to enable.

public void setScrollEnabled(boolean scrollEnabled)

Enables or disables scrolling. Disable is useful during transition.

Parameters:

scrollEnabled: True to enable scroll, false to disable.

public boolean isScrollEnabled()

Returns true if scrolling is enabled, false otherwise.

Returns:

True if scrolling is enabled, false otherwise.

public boolean hasPreviousViewInSameRow(int position)

Returns true if the view at the given position has a same row sibling in front of it. This will return true if first item view is not created.

Parameters:

position: Position in adapter.

Returns:

True if the view at the given position has a same row sibling in front of it.

public void setFocusDrawingOrderEnabled(boolean enabled)

Enables or disables the default "focus draw at last" order rule. Default is enabled.

Parameters:

enabled: True to draw the selected child at last, false otherwise.

public boolean isFocusDrawingOrderEnabled()

Returns true if draws selected child at last, false otherwise. Default is enabled.

Returns:

True if draws selected child at last, false otherwise.

public void setOnTouchInterceptListener(BaseGridView.OnTouchInterceptListener listener)

Sets the touch intercept listener.

Parameters:

listener: The touch intercept listener.

public void setOnMotionInterceptListener(BaseGridView.OnMotionInterceptListener listener)

Sets the generic motion intercept listener.

Parameters:

listener: The motion intercept listener.

public void setOnKeyInterceptListener(BaseGridView.OnKeyInterceptListener listener)

Sets the key intercept listener.

Parameters:

listener: The key intercept listener.

public void setOnUnhandledKeyListener(BaseGridView.OnUnhandledKeyListener listener)

Sets the unhandled key listener.

Parameters:

listener: The unhandled key intercept listener.

public BaseGridView.OnUnhandledKeyListener getOnUnhandledKeyListener()

Returns the unhandled key listener.

Returns:

The unhandled key listener.

public boolean dispatchKeyEvent(KeyEvent event)

public boolean dispatchTouchEvent(MotionEvent event)

protected boolean dispatchGenericFocusedEvent(MotionEvent event)

public final int getSaveChildrenPolicy()

Returns the policy for saving children.

Returns:

policy, one of BaseGridView.SAVE_NO_CHILD BaseGridView.SAVE_ON_SCREEN_CHILD BaseGridView.SAVE_LIMITED_CHILD BaseGridView.SAVE_ALL_CHILD.

public final int getSaveChildrenLimitNumber()

Returns the limit used when when BaseGridView.getSaveChildrenPolicy() is BaseGridView.SAVE_LIMITED_CHILD

public final void setSaveChildrenPolicy(int savePolicy)

Sets the policy for saving children.

Parameters:

savePolicy: One of BaseGridView.SAVE_NO_CHILD BaseGridView.SAVE_ON_SCREEN_CHILD BaseGridView.SAVE_LIMITED_CHILD BaseGridView.SAVE_ALL_CHILD.

public final void setSaveChildrenLimitNumber(int limitNumber)

Sets the limit number when BaseGridView.getSaveChildrenPolicy() is BaseGridView.SAVE_LIMITED_CHILD.

public boolean hasOverlappingRendering()

public void setHasOverlappingRendering(boolean hasOverlapping)

public void onRtlPropertiesChanged(int layoutDirection)

Notify layout manager that layout directionality has been updated

public void setExtraLayoutSpace(int extraLayoutSpace)

Sets pixels of extra space for layout child in invisible area.

Parameters:

extraLayoutSpace: Pixels of extra space for layout invisible child. Must be bigger or equals to 0.

public int getExtraLayoutSpace()

Returns pixels of extra space for layout child in invisible area.

public void animateOut()

Temporarily slide out child views to bottom (for VerticalGridView) or end (for HorizontalGridView). Layout and scrolling will be suppressed until BaseGridView.animateIn() is called.

public void animateIn()

Undo animateOut() and slide in child views.

public void scrollToPosition(int position)

Convenience method to scroll to a certain position. RecyclerView does not implement scrolling logic, rather forwards the call to RecyclerView.LayoutManager.scrollToPosition(int)

Parameters:

position: Scroll to this adapter position

See also: RecyclerView.LayoutManager.scrollToPosition(int)

public void smoothScrollToPosition(int position)

Starts a smooth scroll to an adapter position.

To support smooth scrolling, you must override RecyclerView.LayoutManager and create a RecyclerView.SmoothScroller.

RecyclerView.LayoutManager is responsible for creating the actual scroll action. If you want to provide a custom smooth scroll logic, override RecyclerView.LayoutManager in your LayoutManager.

Parameters:

position: The adapter position to scroll to

See also: RecyclerView.LayoutManager

public final void setSmoothScrollByBehavior(BaseGridView.SmoothScrollByBehavior behavior)

Set custom behavior for smoothScrollBy().

Parameters:

behavior: Custom behavior of SmoothScrollBy(). Null for default behavior.

public BaseGridView.SmoothScrollByBehavior getSmoothScrollByBehavior()

Returns custom behavior for smoothScrollBy().

Returns:

Custom behavior for SmoothScrollBy(). Null for default behavior.

public void smoothScrollBy(int dx, int dy)

Animate a scroll by the given amount of pixels along either axis.

Parameters:

dx: Pixels to scroll horizontally
dy: Pixels to scroll vertically

public void smoothScrollBy(int dx, int dy, Interpolator interpolator)

Animate a scroll by the given amount of pixels along either axis.

Parameters:

dx: Pixels to scroll horizontally
dy: Pixels to scroll vertically
interpolator: to be used for scrolling. If it is null, RecyclerView will use an internal default interpolator.

public final void setSmoothScrollSpeedFactor(float smoothScrollSpeedFactor)

Set factor of how slow the smoothScroller should run. For example when set to 2f, the smooth scroller is twice slower. The value is 1f by default.

Parameters:

smoothScrollSpeedFactor: Factor of how slow the smooth scroll is.

public final float getSmoothScrollSpeedFactor()

Returns:

Factor of how slow the smoothScroller runs. Default value is 1f.

public final void setSmoothScrollMaxPendingMoves(int maxPendingMoves)

When holding DPAD, DPAD events are generated faster than the grid view can scroll. The grid view counts unhandled DPAD events and completes the movement after user release DPAD. If the value is set too high, the scrolling will last very long after DPAD is released. If the value is set too low, it may miss many DPAD events. The default value is 10. If app increases BaseGridView.setSmoothScrollSpeedFactor(float), it may need decrease the max pending DPAD events to avoid scrolling too long after DPAD release.

Parameters:

maxPendingMoves: Maximum number of pending DPAD events to be remembered.

public final int getSmoothScrollMaxPendingMoves()

When holding DPAD, DPAD events are generated faster than the grid view can scroll. The grid view counts unhandled DPAD events and complete the movement after user release DPAD. If the value is set too high, the scrolling will last very long after DPAD is released. If the value is set too low, it may miss many DPAD events. The default value is 10. If app increases BaseGridView.setSmoothScrollSpeedFactor(float), it may need decrease the max pending DPAD events to avoid scrolling too long after DPAD release.

Returns:

Maximum number of pending DPAD events to be remembered when smooth scroll cannot catch up speed of DPAD events being sent.

public void setInitialPrefetchItemCount(int itemCount)

Sets the number of items to prefetch in , which defines how many inner items should be prefetched when this GridView is nested inside another RecyclerView.

Set this value to the number of items this inner GridView will display when it is first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items so they are ready, avoiding jank as the inner GridView is scrolled into the viewport.

For example, take a VerticalGridView of scrolling HorizontalGridViews. The rows always have 6 items visible in them (or 7 if not aligned). Passing 6 to this method for each inner GridView will enable RecyclerView's prefetching feature to do create/bind work for 6 views within a row early, before it is scrolled on screen, instead of just the default 4.

Calling this method does nothing unless the LayoutManager is in a RecyclerView nested in another RecyclerView.

Note: Setting this value to be larger than the number of views that will be visible in this view can incur unnecessary bind work, and an increase to the number of Views created and in active use.

Parameters:

itemCount: Number of items to prefetch

See also: BaseGridView.getInitialPrefetchItemCount()

public int getInitialPrefetchItemCount()

Gets the number of items to prefetch in , which defines how many inner items should be prefetched when this GridView is nested inside another RecyclerView.

Returns:

number of items to prefetch.

See also: , BaseGridView.setInitialPrefetchItemCount(int)

public void removeView(View view)

public void removeViewAt(int index)

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.leanback.widget;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;

/**
 * An abstract base class for vertically and horizontally scrolling lists. The items come
 * from the {@link RecyclerView.Adapter} associated with this view.
 * Do not directly use this class, use {@link VerticalGridView} and {@link HorizontalGridView}.
 * The class is not intended to be subclassed other than {@link VerticalGridView} and
 * {@link HorizontalGridView}.
 */
public abstract class BaseGridView extends RecyclerView {

    /**
     * Always keep focused item at a aligned position.  Developer can use
     * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
     * In this mode, the last focused position will be remembered and restored when focus
     * is back to the view.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public static final int FOCUS_SCROLL_ALIGNED = 0;

    /**
     * Scroll to make the focused item inside client area.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public static final int FOCUS_SCROLL_ITEM = 1;

    /**
     * Scroll a page of items when focusing to item outside the client area.
     * The page size matches the client area size of RecyclerView.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public static final int FOCUS_SCROLL_PAGE = 2;

    /**
     * The first item is aligned with the low edge of the viewport. When
     * navigating away from the first item, the focus item is aligned to a key line location.
     * <p>
     * For HorizontalGridView, low edge refers to getPaddingLeft() when RTL is false or
     * getWidth() - getPaddingRight() when RTL is true.
     * For VerticalGridView, low edge refers to getPaddingTop().
     * <p>
     * The key line location is calculated by "windowAlignOffset" and
     * "windowAlignOffsetPercent"; if neither of these two is defined, the
     * default value is 1/2 of the size.
     * <p>
     * Note if there are very few items between low edge and key line, use
     * {@link #setWindowAlignmentPreferKeyLineOverLowEdge(boolean)} to control whether you prefer
     * to align the items to key line or low edge. Default is preferring low edge.
     */
    public static final int WINDOW_ALIGN_LOW_EDGE = 1;

    /**
     * The last item is aligned with the high edge of the viewport when
     * navigating to the end of list. When navigating away from the end, the
     * focus item is aligned to a key line location.
     * <p>
     * For HorizontalGridView, high edge refers to getWidth() - getPaddingRight() when RTL is false
     * or getPaddingLeft() when RTL is true.
     * For VerticalGridView, high edge refers to getHeight() - getPaddingBottom().
     * <p>
     * The key line location is calculated by "windowAlignOffset" and
     * "windowAlignOffsetPercent"; if neither of these two is defined, the
     * default value is 1/2 of the size.
     * <p>
     * Note if there are very few items between high edge and key line, use
     * {@link #setWindowAlignmentPreferKeyLineOverHighEdge(boolean)} to control whether you prefer
     * to align the items to key line or high edge. Default is preferring key line.
     */
    public static final int WINDOW_ALIGN_HIGH_EDGE = 1 << 1;

    /**
     * The first item and last item are aligned with the two edges of the
     * viewport. When navigating in the middle of list, the focus maintains a
     * key line location.
     * <p>
     * The key line location is calculated by "windowAlignOffset" and
     * "windowAlignOffsetPercent"; if neither of these two is defined, the
     * default value is 1/2 of the size.
     */
    public static final int WINDOW_ALIGN_BOTH_EDGE =
            WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;

    /**
     * The focused item always stays in a key line location.
     * <p>
     * The key line location is calculated by "windowAlignOffset" and
     * "windowAlignOffsetPercent"; if neither of these two is defined, the
     * default value is 1/2 of the size.
     */
    public static final int WINDOW_ALIGN_NO_EDGE = 0;

    /**
     * Value indicates that percent is not used.
     */
    public static final float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1;

    /**
     * Value indicates that percent is not used.
     */
    public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED =
            ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;

    /**
     * Dont save states of any child views.
     */
    public static final int SAVE_NO_CHILD = 0;

    /**
     * Only save on screen child views, the states are lost when they become off screen.
     */
    public static final int SAVE_ON_SCREEN_CHILD = 1;

    /**
     * Save on screen views plus save off screen child views states up to
     * {@link #getSaveChildrenLimitNumber()}.
     */
    public static final int SAVE_LIMITED_CHILD = 2;

    /**
     * Save on screen views plus save off screen child views without any limitation.
     * This might cause out of memory, only use it when you are dealing with limited data.
     */
    public static final int SAVE_ALL_CHILD = 3;

    private static final int PFLAG_RETAIN_FOCUS_FOR_CHILD = 1;

    /**
     * Defines behavior of duration and interpolator for smoothScrollBy().
     */
    public interface SmoothScrollByBehavior {
        /**
         * Defines duration in milliseconds of smoothScrollBy().
         *
         * @param dx x distance in pixels.
         * @param dy y distance in pixels.
         * @return Duration in milliseconds or UNDEFINED_DURATION for default value.
         */
        int configSmoothScrollByDuration(int dx, int dy);

        /**
         * Defines interpolator of smoothScrollBy().
         *
         * @param dx x distance in pixels.
         * @param dy y distance in pixels.
         * @return Interpolator to be used or null for default interpolator.
         */
        @Nullable
        Interpolator configSmoothScrollByInterpolator(int dx, int dy);
    }

    /**
     * Listener for intercepting touch dispatch events.
     */
    public interface OnTouchInterceptListener {
        /**
         * Returns true if the touch dispatch event should be consumed.
         */
        boolean onInterceptTouchEvent(@NonNull MotionEvent event);
    }

    /**
     * Listener for intercepting generic motion dispatch events.
     */
    public interface OnMotionInterceptListener {
        /**
         * Returns true if the touch dispatch event should be consumed.
         */
        boolean onInterceptMotionEvent(@NonNull MotionEvent event);
    }

    /**
     * Listener for intercepting key dispatch events.
     */
    public interface OnKeyInterceptListener {
        /**
         * Returns true if the key dispatch event should be consumed.
         */
        boolean onInterceptKeyEvent(@NonNull KeyEvent event);
    }

    /**
     * Listener for intercepting unhandled key events.
     */
    public interface OnUnhandledKeyListener {
        /**
         * Returns true if the key event should be consumed.
         */
        boolean onUnhandledKey(@NonNull KeyEvent event);
    }

    /**
     * Interface for receiving notification when BaseGridView has completed a full layout
     * calculation.
     */
    public interface OnLayoutCompletedListener {

        /**
         * Called after a full layout calculation is finished.
         *
         * @param state Transient state of RecyclerView
         */
        void onLayoutCompleted(@NonNull RecyclerView.State state);
    }

    GridLayoutManager mLayoutManager;

    private SmoothScrollByBehavior mSmoothScrollByBehavior;

    /**
     * Animate layout changes from a child resizing or adding/removing a child.
     */
    private boolean mAnimateChildLayout = true;

    private boolean mHasOverlappingRendering = true;

    private RecyclerView.ItemAnimator mSavedItemAnimator;

    private OnTouchInterceptListener mOnTouchInterceptListener;
    private OnMotionInterceptListener mOnMotionInterceptListener;
    private OnKeyInterceptListener mOnKeyInterceptListener;
    private OnUnhandledKeyListener mOnUnhandledKeyListener;

    /**
     * Number of items to prefetch when first coming on screen with new data.
     */
    int mInitialPrefetchItemCount = 4;

    private int mPrivateFlag;

    BaseGridView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mLayoutManager = new GridLayoutManager(this);
        setLayoutManager(mLayoutManager);
        // leanback LayoutManager already restores focus inside onLayoutChildren().
        setPreserveFocusAfterLayout(false);
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setHasFixedSize(true);
        setChildrenDrawingOrderEnabled(true);
        setWillNotDraw(true);
        setOverScrollMode(View.OVER_SCROLL_NEVER);
        // Disable change animation by default on leanback.
        // Change animation will create a new view and cause undesired
        // focus animation between the old view and new view.
        ((SimpleItemAnimator) getItemAnimator()).setSupportsChangeAnimations(false);
        super.addRecyclerListener(new RecyclerView.RecyclerListener() {
            @Override
            public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
                mLayoutManager.onChildRecycled(holder);
            }
        });
    }

    @SuppressLint("CustomViewStyleable")
    void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
        boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
        boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false);
        mLayoutManager.setFocusOutAllowed(throughFront, throughEnd);
        boolean throughSideStart = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideStart, true);
        boolean throughSideEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideEnd, true);
        mLayoutManager.setFocusOutSideAllowed(throughSideStart, throughSideEnd);
        mLayoutManager.setVerticalSpacing(
                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_verticalSpacing,
                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)));
        mLayoutManager.setHorizontalSpacing(
                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_horizontalSpacing,
                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)));
        if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
            setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
        }
        a.recycle();
    }

    /**
     * Sets the strategy used to scroll in response to item focus changing:
     * <ul>
     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
     * </ul>
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void setFocusScrollStrategy(int scrollStrategy) {
        if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM
                && scrollStrategy != FOCUS_SCROLL_PAGE) {
            throw new IllegalArgumentException("Invalid scrollStrategy");
        }
        mLayoutManager.setFocusScrollStrategy(scrollStrategy);
        requestLayout();
    }

    /**
     * Returns the strategy used to scroll in response to item focus changing.
     * <ul>
     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
     * </ul>
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public int getFocusScrollStrategy() {
        return mLayoutManager.getFocusScrollStrategy();
    }

    /**
     * Sets the method for focused item alignment in the view.
     *
     * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE},
     *                        {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or
     *                        {@link #WINDOW_ALIGN_NO_EDGE}.
     */
    public void setWindowAlignment(int windowAlignment) {
        mLayoutManager.setWindowAlignment(windowAlignment);
        requestLayout();
    }

    /**
     * Returns the method for focused item alignment in the view.
     *
     * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE},
     * {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}.
     */
    public int getWindowAlignment() {
        return mLayoutManager.getWindowAlignment();
    }

    /**
     * Sets whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
     * When true, if there are very few items between low edge and key line, align items to key
     * line instead of align items to low edge.
     * Default value is false (aka prefer align to low edge).
     *
     * @param preferKeyLineOverLowEdge True to prefer key line over low edge, false otherwise.
     */
    public void setWindowAlignmentPreferKeyLineOverLowEdge(boolean preferKeyLineOverLowEdge) {
        mLayoutManager.mWindowAlignment.mainAxis()
                .setPreferKeylineOverLowEdge(preferKeyLineOverLowEdge);
        requestLayout();
    }


    /**
     * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
     * When true, if there are very few items between high edge and key line, align items to key
     * line instead of align items to high edge.
     * Default value is true (aka prefer align to key line).
     *
     * @param preferKeyLineOverHighEdge True to prefer key line over high edge, false otherwise.
     */
    public void setWindowAlignmentPreferKeyLineOverHighEdge(boolean preferKeyLineOverHighEdge) {
        mLayoutManager.mWindowAlignment.mainAxis()
                .setPreferKeylineOverHighEdge(preferKeyLineOverHighEdge);
        requestLayout();
    }

    /**
     * Returns whether prefer key line over low edge when {@link #WINDOW_ALIGN_LOW_EDGE} is used.
     * When true, if there are very few items between low edge and key line, align items to key
     * line instead of align items to low edge.
     * Default value is false (aka prefer align to low edge).
     *
     * @return True to prefer key line over low edge, false otherwise.
     */
    public boolean isWindowAlignmentPreferKeyLineOverLowEdge() {
        return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverLowEdge();
    }


    /**
     * Returns whether prefer key line over high edge when {@link #WINDOW_ALIGN_HIGH_EDGE} is used.
     * When true, if there are very few items between high edge and key line, align items to key
     * line instead of align items to high edge.
     * Default value is true (aka prefer align to key line).
     *
     * @return True to prefer key line over high edge, false otherwise.
     */
    public boolean isWindowAlignmentPreferKeyLineOverHighEdge() {
        return mLayoutManager.mWindowAlignment.mainAxis().isPreferKeylineOverHighEdge();
    }


    /**
     * Sets the offset in pixels for window alignment key line.
     *
     * @param offset The number of pixels to offset.  If the offset is positive,
     *               it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
     *               if the offset is negative, the absolute value is distance from high
     *               edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
     *               Default value is 0.
     */
    public void setWindowAlignmentOffset(int offset) {
        mLayoutManager.setWindowAlignmentOffset(offset);
        requestLayout();
    }

    /**
     * Returns the offset in pixels for window alignment key line.
     *
     * @return The number of pixels to offset.  If the offset is positive,
     * it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
     * if the offset is negative, the absolute value is distance from high
     * edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
     * Default value is 0.
     */
    public int getWindowAlignmentOffset() {
        return mLayoutManager.getWindowAlignmentOffset();
    }

    /**
     * Sets the offset percent for window alignment key line in addition to {@link
     * #getWindowAlignmentOffset()}.
     *
     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
     *                      width from low edge. Use
     *                      {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
     *                      Default value is 50.
     */
    public void setWindowAlignmentOffsetPercent(float offsetPercent) {
        mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent);
        requestLayout();
    }

    /**
     * Returns the offset percent for window alignment key line in addition to
     * {@link #getWindowAlignmentOffset()}.
     *
     * @return Percentage to offset. E.g., 40 means 40% of the width from the
     * low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if
     * disabled. Default value is 50.
     */
    public float getWindowAlignmentOffsetPercent() {
        return mLayoutManager.getWindowAlignmentOffsetPercent();
    }

    /**
     * Sets number of pixels to the end of low edge. Supports right to left layout direction.
     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
     *
     * @param offset In left to right or vertical case, it's the offset added to left/top edge.
     *               In right to left case, it's the offset subtracted from right edge.
     */
    public void setItemAlignmentOffset(int offset) {
        mLayoutManager.setItemAlignmentOffset(offset);
        requestLayout();
    }

    /**
     * Returns number of pixels to the end of low edge. Supports right to left layout direction. In
     * left to right or vertical case, it's the offset added to left/top edge. In right to left
     * case, it's the offset subtracted from right edge.
     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
     *
     * @return The number of pixels to the end of low edge.
     */
    public int getItemAlignmentOffset() {
        return mLayoutManager.getItemAlignmentOffset();
    }

    /**
     * Sets whether applies padding to item alignment when {@link #getItemAlignmentOffsetPercent()}
     * is 0 or 100.
     * <p>When true:
     * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
     * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
     * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
     * </p>
     * <p>When false: does not apply padding</p>
     */
    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
        mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
        requestLayout();
    }

    /**
     * Returns true if applies padding to item alignment when
     * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
     * <p>When true:
     * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
     * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
     * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
     * </p>
     * <p>When false: does not apply padding</p>
     */
    public boolean isItemAlignmentOffsetWithPadding() {
        return mLayoutManager.isItemAlignmentOffsetWithPadding();
    }

    /**
     * Sets the offset percent for item alignment in addition to {@link
     * #getItemAlignmentOffset()}.
     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
     *
     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
     *                      width from the low edge. Use
     *                      {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
     */
    public void setItemAlignmentOffsetPercent(float offsetPercent) {
        mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent);
        requestLayout();
    }

    /**
     * Returns the offset percent for item alignment in addition to {@link
     * #getItemAlignmentOffset()}.
     *
     * @return Percentage to offset. E.g., 40 means 40% of the width from the
     * low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if
     * disabled. Default value is 50.
     */
    public float getItemAlignmentOffsetPercent() {
        return mLayoutManager.getItemAlignmentOffsetPercent();
    }

    /**
     * Sets the id of the view to align with. Use {@link android.view.View#NO_ID} (default)
     * for the root {@link RecyclerView.ViewHolder#itemView}.
     * Item alignment settings on BaseGridView are if {@link ItemAlignmentFacet}
     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
     */
    public void setItemAlignmentViewId(int viewId) {
        mLayoutManager.setItemAlignmentViewId(viewId);
    }

    /**
     * Returns the id of the view to align with, or {@link android.view.View#NO_ID} for the root
     * {@link RecyclerView.ViewHolder#itemView}.
     *
     * @return The id of the view to align with, or {@link android.view.View#NO_ID} for the root
     * {@link RecyclerView.ViewHolder#itemView}.
     */
    public int getItemAlignmentViewId() {
        return mLayoutManager.getItemAlignmentViewId();
    }

    /**
     * Sets the spacing in pixels between two child items.
     *
     * @deprecated use {@link #setItemSpacing(int)}
     */
    @Deprecated
    public void setItemMargin(int margin) {
        setItemSpacing(margin);
    }

    /**
     * Sets the vertical and horizontal spacing in pixels between two child items.
     *
     * @param spacing Vertical and horizontal spacing in pixels between two child items.
     */
    public void setItemSpacing(int spacing) {
        mLayoutManager.setItemSpacing(spacing);
        requestLayout();
    }

    /**
     * Sets the spacing in pixels between two child items vertically.
     *
     * @deprecated Use {@link #setVerticalSpacing(int)}
     */
    @Deprecated
    public void setVerticalMargin(int margin) {
        setVerticalSpacing(margin);
    }

    /**
     * Returns the spacing in pixels between two child items vertically.
     *
     * @deprecated Use {@link #getVerticalSpacing()}
     */
    @Deprecated
    public int getVerticalMargin() {
        return mLayoutManager.getVerticalSpacing();
    }

    /**
     * Sets the spacing in pixels between two child items horizontally.
     *
     * @deprecated Use {@link #setHorizontalSpacing(int)}
     */
    @Deprecated
    public void setHorizontalMargin(int margin) {
        setHorizontalSpacing(margin);
    }

    /**
     * Returns the spacing in pixels between two child items horizontally.
     *
     * @deprecated Use {@link #getHorizontalSpacing()}
     */
    @Deprecated
    public int getHorizontalMargin() {
        return mLayoutManager.getHorizontalSpacing();
    }

    /**
     * Sets the vertical spacing in pixels between two child items.
     *
     * @param spacing Vertical spacing between two child items.
     */
    public void setVerticalSpacing(int spacing) {
        mLayoutManager.setVerticalSpacing(spacing);
        requestLayout();
    }

    /**
     * Returns the vertical spacing in pixels between two child items.
     *
     * @return The vertical spacing in pixels between two child items.
     */
    public int getVerticalSpacing() {
        return mLayoutManager.getVerticalSpacing();
    }

    /**
     * Sets the horizontal spacing in pixels between two child items.
     *
     * @param spacing Horizontal spacing in pixels between two child items.
     */
    public void setHorizontalSpacing(int spacing) {
        mLayoutManager.setHorizontalSpacing(spacing);
        requestLayout();
    }

    /**
     * Returns the horizontal spacing in pixels between two child items.
     *
     * @return The Horizontal spacing in pixels between two child items.
     */
    public int getHorizontalSpacing() {
        return mLayoutManager.getHorizontalSpacing();
    }

    /**
     * Registers a callback to be invoked when an item in BaseGridView has
     * been laid out.
     *
     * @param listener The listener to be invoked.
     */
    public void setOnChildLaidOutListener(@Nullable OnChildLaidOutListener listener) {
        mLayoutManager.setOnChildLaidOutListener(listener);
    }

    /**
     * Registers a callback to be invoked when an item in BaseGridView has
     * been selected.  Note that the listener may be invoked when there is a
     * layout pending on the view, affording the listener an opportunity to
     * adjust the upcoming layout based on the selection state.
     *
     * @param listener The listener to be invoked.
     */
    @SuppressLint("ReferencesDeprecated")
    @SuppressWarnings("deprecation")
    public void setOnChildSelectedListener(@Nullable OnChildSelectedListener listener) {
        mLayoutManager.setOnChildSelectedListener(listener);
    }

    /**
     * Registers a callback to be invoked when the BaseGridView completes a full layout calculation.
     *
     * @param listener The listener to be invoked.
     */
    public final void addOnLayoutCompletedListener(@NonNull OnLayoutCompletedListener listener) {
        mLayoutManager.addOnLayoutCompletedListener(listener);
    }

    /**
     * Removes a callback to be invoked when the BaseGridView completes a full layout calculation.
     *
     * @param listener The listener to be invoked.
     */
    public final void removeOnLayoutCompletedListener(@NonNull OnLayoutCompletedListener listener) {
        mLayoutManager.removeOnLayoutCompletedListener(listener);
    }

    /**
     * Registers a callback to be invoked when an item in BaseGridView has
     * been selected.  Note that the listener may be invoked when there is a
     * layout pending on the view, affording the listener an opportunity to
     * adjust the upcoming layout based on the selection state.
     * This method will clear all existing listeners added by
     * {@link #addOnChildViewHolderSelectedListener}.
     *
     * @param listener The listener to be invoked.
     */
    public void setOnChildViewHolderSelectedListener(
            @Nullable OnChildViewHolderSelectedListener listener) {
        mLayoutManager.setOnChildViewHolderSelectedListener(listener);
    }

    /**
     * Registers a callback to be invoked when an item in BaseGridView has
     * been selected.  Note that the listener may be invoked when there is a
     * layout pending on the view, affording the listener an opportunity to
     * adjust the upcoming layout based on the selection state.
     *
     * @param listener The listener to be invoked.
     */
    public void addOnChildViewHolderSelectedListener(
            @NonNull OnChildViewHolderSelectedListener listener) {
        mLayoutManager.addOnChildViewHolderSelectedListener(listener);
    }

    /**
     * Remove the callback invoked when an item in BaseGridView has been selected.
     *
     * @param listener The listener to be removed.
     */
    public void removeOnChildViewHolderSelectedListener(
            @NonNull OnChildViewHolderSelectedListener listener) {
        mLayoutManager.removeOnChildViewHolderSelectedListener(listener);
    }

    /**
     * Changes the selected item immediately without animation.
     */
    public void setSelectedPosition(int position) {
        mLayoutManager.setSelection(position, 0);
    }

    /**
     * Changes the selected item and/or subposition immediately without animation.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void setSelectedPositionWithSub(int position, int subposition) {
        mLayoutManager.setSelectionWithSub(position, subposition, 0);
    }

    /**
     * Changes the selected item immediately without animation, scrollExtra is
     * applied in primary scroll direction.  The scrollExtra will be kept until
     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
     */
    public void setSelectedPosition(int position, int scrollExtra) {
        mLayoutManager.setSelection(position, scrollExtra);
    }

    /**
     * Changes the selected item and/or subposition immediately without animation, scrollExtra is
     * applied in primary scroll direction.  The scrollExtra will be kept until
     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void setSelectedPositionWithSub(int position, int subposition, int scrollExtra) {
        mLayoutManager.setSelectionWithSub(position, subposition, scrollExtra);
    }

    /**
     * Changes the selected item and run an animation to scroll to the target
     * position.
     *
     * @param position Adapter position of the item to select.
     */
    public void setSelectedPositionSmooth(int position) {
        mLayoutManager.setSelectionSmooth(position);
    }

    /**
     * Changes the selected item and/or subposition, runs an animation to scroll to the target
     * position.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void setSelectedPositionSmoothWithSub(int position, int subposition) {
        mLayoutManager.setSelectionSmoothWithSub(position, subposition);
    }

    /**
     * Perform a task on ViewHolder at given position after smooth scrolling to it.
     *
     * @param position Position of item in adapter.
     * @param task     Task to executed on the ViewHolder at a given position.
     */
    @SuppressWarnings("deprecation")
    public void setSelectedPositionSmooth(final int position, @Nullable final ViewHolderTask task) {
        if (task != null) {
            RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
            if (vh == null || hasPendingAdapterUpdates()) {
                addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
                    @Override
                    public void onChildViewHolderSelected(@NonNull RecyclerView parent,
                            RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
                        if (selectedPosition == position) {
                            removeOnChildViewHolderSelectedListener(this);
                            task.run(child);
                        }
                    }
                });
            } else {
                task.run(vh);
            }
        }
        setSelectedPositionSmooth(position);
    }

    /**
     * Perform a task on ViewHolder at given position after scroll to it.
     *
     * @param position Position of item in adapter.
     * @param task     Task to executed on the ViewHolder at a given position.
     */
    @SuppressWarnings("deprecation")
    public void setSelectedPosition(final int position, @Nullable final ViewHolderTask task) {
        if (task != null) {
            RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
            if (vh == null || hasPendingAdapterUpdates()) {
                addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
                    @Override
                    public void onChildViewHolderSelectedAndPositioned(@NonNull RecyclerView parent,
                            RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
                        if (selectedPosition == position) {
                            removeOnChildViewHolderSelectedListener(this);
                            task.run(child);
                        }
                    }
                });
            } else {
                task.run(vh);
            }
        }
        setSelectedPosition(position);
    }

    /**
     * Returns the adapter position of selected item.
     *
     * @return The adapter position of selected item.
     */
    public int getSelectedPosition() {
        return mLayoutManager.getSelection();
    }

    /**
     * Returns the sub selected item position started from zero.  An item can have
     * multiple {@link ItemAlignmentFacet}s provided by {@link RecyclerView.ViewHolder}
     * or {@link FacetProviderAdapter}.  Zero is returned when no {@link ItemAlignmentFacet}
     * is defined.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public int getSelectedSubPosition() {
        return mLayoutManager.getSubSelection();
    }

    /**
     * Sets whether ItemAnimator should run when a child changes size or when adding
     * or removing a child.
     *
     * @param animateChildLayout True to enable ItemAnimator, false to disable.
     */
    public void setAnimateChildLayout(boolean animateChildLayout) {
        if (mAnimateChildLayout != animateChildLayout) {
            mAnimateChildLayout = animateChildLayout;
            if (!mAnimateChildLayout) {
                mSavedItemAnimator = getItemAnimator();
                super.setItemAnimator(null);
            } else {
                super.setItemAnimator(mSavedItemAnimator);
            }
        }
    }

    /**
     * Returns true if an animation will run when a child changes size or when
     * adding or removing a child.
     *
     * @return True if ItemAnimator is enabled, false otherwise.
     */
    public boolean isChildLayoutAnimated() {
        return mAnimateChildLayout;
    }

    /**
     * Sets the gravity used for child view positioning. Defaults to
     * GRAVITY_TOP|GRAVITY_START.
     *
     * @param gravity See {@link android.view.Gravity}
     */
    public void setGravity(int gravity) {
        mLayoutManager.setGravity(gravity);
        requestLayout();
    }

    @Override
    public void setLayoutManager(@Nullable RecyclerView.LayoutManager layout) {
        if (layout == null) {
            super.setLayoutManager(null);
            if (mLayoutManager != null) {
                mLayoutManager.setGridView(null);
            }
            mLayoutManager = null;
            return;
        }

        mLayoutManager = (GridLayoutManager) layout;
        mLayoutManager.setGridView(this);
        super.setLayoutManager(layout);
    }

    @Override
    public boolean onRequestFocusInDescendants(int direction,
            @Nullable Rect previouslyFocusedRect) {
        if ((mPrivateFlag & PFLAG_RETAIN_FOCUS_FOR_CHILD) == PFLAG_RETAIN_FOCUS_FOR_CHILD) {
            // dont focus to child if GridView itself retains focus for child
            return false;
        }
        return mLayoutManager.gridOnRequestFocusInDescendants(this, direction,
                previouslyFocusedRect);
    }

    /**
     * Returns the x/y offsets to final position from current position if the view
     * is selected.
     *
     * @param view    The view to get offsets.
     * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of Y.
     */
    public void getViewSelectedOffsets(@NonNull View view, @NonNull int[] offsets) {
        mLayoutManager.getViewSelectedOffsets(view, offsets);
    }

    @Override
    public int getChildDrawingOrder(int childCount, int i) {
        return mLayoutManager.getChildDrawingOrder(this, childCount, i);
    }

    final boolean isChildrenDrawingOrderEnabledInternal() {
        return isChildrenDrawingOrderEnabled();
    }

    @Override
    @Nullable
    public View focusSearch(int direction) {
        if (isFocused()) {
            // focusSearch(int) is called when GridView itself is focused.
            // Calling focusSearch(view, int) to get next sibling of current selected child.
            View view = mLayoutManager.findViewByPosition(mLayoutManager.getSelection());
            if (view != null) {
                return focusSearch(view, direction);
            }
        }
        // otherwise, go to mParent to perform focusSearch
        return super.focusSearch(direction);
    }

    @Override
    protected void onFocusChanged(boolean gainFocus, int direction,
            @Nullable Rect previouslyFocusedRect) {
        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
        mLayoutManager.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    }

    /**
     * Disables or enables focus search.
     *
     * @param disabled True to disable focus search, false to enable.
     */
    public final void setFocusSearchDisabled(boolean disabled) {
        // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
        // re-gain focus after a BACK key pressed, so block children focus during transition.
        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS : FOCUS_AFTER_DESCENDANTS);
        mLayoutManager.setFocusSearchDisabled(disabled);
    }

    /**
     * Returns true if focus search is disabled.
     *
     * @return True if focus search is disabled.
     */
    public final boolean isFocusSearchDisabled() {
        return mLayoutManager.isFocusSearchDisabled();
    }

    /**
     * Enables or disables layout.  All children will be removed when layout is
     * disabled.
     *
     * @param layoutEnabled True to enable layout, false otherwise.
     */
    public void setLayoutEnabled(boolean layoutEnabled) {
        mLayoutManager.setLayoutEnabled(layoutEnabled);
    }

    /**
     * Changes and overrides children's visibility.
     *
     * @param visibility See {@link View#getVisibility()}.
     */
    public void setChildrenVisibility(int visibility) {
        mLayoutManager.setChildrenVisibility(visibility);
    }

    /**
     * Enables or disables pruning of children.  Disable is useful during transition.
     *
     * @param pruneChild True to prune children out side visible area, false to enable.
     */
    public void setPruneChild(boolean pruneChild) {
        mLayoutManager.setPruneChild(pruneChild);
    }

    /**
     * Enables or disables scrolling.  Disable is useful during transition.
     *
     * @param scrollEnabled True to enable scroll, false to disable.
     */
    public void setScrollEnabled(boolean scrollEnabled) {
        mLayoutManager.setScrollEnabled(scrollEnabled);
    }

    /**
     * Returns true if scrolling is enabled, false otherwise.
     *
     * @return True if scrolling is enabled, false otherwise.
     */
    public boolean isScrollEnabled() {
        return mLayoutManager.isScrollEnabled();
    }

    /**
     * Returns true if the view at the given position has a same row sibling
     * in front of it.  This will return true if first item view is not created.
     *
     * @param position Position in adapter.
     * @return True if the view at the given position has a same row sibling in front of it.
     */
    public boolean hasPreviousViewInSameRow(int position) {
        return mLayoutManager.hasPreviousViewInSameRow(position);
    }

    /**
     * Enables or disables the default "focus draw at last" order rule. Default is enabled.
     *
     * @param enabled True to draw the selected child at last, false otherwise.
     */
    public void setFocusDrawingOrderEnabled(boolean enabled) {
        super.setChildrenDrawingOrderEnabled(enabled);
    }

    /**
     * Returns true if draws selected child at last, false otherwise. Default is enabled.
     *
     * @return True if draws selected child at last, false otherwise.
     */
    public boolean isFocusDrawingOrderEnabled() {
        return super.isChildrenDrawingOrderEnabled();
    }

    /**
     * Sets the touch intercept listener.
     *
     * @param listener The touch intercept listener.
     */
    public void setOnTouchInterceptListener(@Nullable OnTouchInterceptListener listener) {
        mOnTouchInterceptListener = listener;
    }

    /**
     * Sets the generic motion intercept listener.
     *
     * @param listener The motion intercept listener.
     */
    public void setOnMotionInterceptListener(@Nullable OnMotionInterceptListener listener) {
        mOnMotionInterceptListener = listener;
    }

    /**
     * Sets the key intercept listener.
     *
     * @param listener The key intercept listener.
     */
    public void setOnKeyInterceptListener(@Nullable OnKeyInterceptListener listener) {
        mOnKeyInterceptListener = listener;
    }

    /**
     * Sets the unhandled key listener.
     *
     * @param listener The unhandled key intercept listener.
     */
    public void setOnUnhandledKeyListener(@Nullable OnUnhandledKeyListener listener) {
        mOnUnhandledKeyListener = listener;
    }

    /**
     * Returns the unhandled key listener.
     *
     * @return The unhandled key listener.
     */
    @Nullable
    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
        return mOnUnhandledKeyListener;
    }

    @Override
    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
        if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
            return true;
        }
        if (super.dispatchKeyEvent(event)) {
            return true;
        }
        return mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event);
    }

    @Override
    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
        if (mOnTouchInterceptListener != null) {
            if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
                return true;
            }
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    protected boolean dispatchGenericFocusedEvent(@NonNull MotionEvent event) {
        if (mOnMotionInterceptListener != null) {
            if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
                return true;
            }
        }
        return super.dispatchGenericFocusedEvent(event);
    }

    /**
     * Returns the policy for saving children.
     *
     * @return policy, one of {@link #SAVE_NO_CHILD}
     * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
     */
    public final int getSaveChildrenPolicy() {
        return mLayoutManager.mChildrenStates.getSavePolicy();
    }

    /**
     * Returns the limit used when when {@link #getSaveChildrenPolicy()} is
     * {@link #SAVE_LIMITED_CHILD}
     */
    public final int getSaveChildrenLimitNumber() {
        return mLayoutManager.mChildrenStates.getLimitNumber();
    }

    /**
     * Sets the policy for saving children.
     *
     * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
     *                   {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
     */
    public final void setSaveChildrenPolicy(int savePolicy) {
        mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
    }

    /**
     * Sets the limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
     */
    public final void setSaveChildrenLimitNumber(int limitNumber) {
        mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
    }

    @Override
    public boolean hasOverlappingRendering() {
        return mHasOverlappingRendering;
    }

    public void setHasOverlappingRendering(boolean hasOverlapping) {
        mHasOverlappingRendering = hasOverlapping;
    }

    /**
     * Notify layout manager that layout directionality has been updated
     */
    @Override
    public void onRtlPropertiesChanged(int layoutDirection) {
        if (mLayoutManager != null) {
            mLayoutManager.onRtlPropertiesChanged(layoutDirection);
        }
    }

    /**
     * Sets pixels of extra space for layout child in invisible area.
     *
     * @param extraLayoutSpace Pixels of extra space for layout invisible child.
     *                         Must be bigger or equals to 0.
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void setExtraLayoutSpace(int extraLayoutSpace) {
        mLayoutManager.setExtraLayoutSpace(extraLayoutSpace);
    }

    /**
     * Returns pixels of extra space for layout child in invisible area.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public int getExtraLayoutSpace() {
        return mLayoutManager.getExtraLayoutSpace();
    }

    /**
     * Temporarily slide out child views to bottom (for VerticalGridView) or end
     * (for HorizontalGridView). Layout and scrolling will be suppressed until
     * {@link #animateIn()} is called.
     */
    public void animateOut() {
        mLayoutManager.slideOut();
    }

    /**
     * Undo animateOut() and slide in child views.
     */
    public void animateIn() {
        mLayoutManager.slideIn();
    }

    @Override
    public void scrollToPosition(int position) {
        // dont abort the animateOut() animation, just record the position
        if (mLayoutManager.isSlidingChildViews()) {
            mLayoutManager.setSelectionWithSub(position, 0, 0);
            return;
        }
        super.scrollToPosition(position);
    }

    @Override
    public void smoothScrollToPosition(int position) {
        // dont abort the animateOut() animation, just record the position
        if (mLayoutManager.isSlidingChildViews()) {
            mLayoutManager.setSelectionWithSub(position, 0, 0);
            return;
        }
        super.smoothScrollToPosition(position);
    }

    /**
     * Set custom behavior for smoothScrollBy().
     *
     * @param behavior Custom behavior of SmoothScrollBy(). Null for default behavior.
     */
    public final void setSmoothScrollByBehavior(@Nullable SmoothScrollByBehavior behavior) {
        mSmoothScrollByBehavior = behavior;
    }

    /**
     * Returns custom behavior for smoothScrollBy().
     *
     * @return Custom behavior for SmoothScrollBy(). Null for default behavior.
     */
    @Nullable
    public SmoothScrollByBehavior getSmoothScrollByBehavior() {
        return mSmoothScrollByBehavior;
    }

    @Override
    public void smoothScrollBy(int dx, int dy) {
        if (mSmoothScrollByBehavior != null) {
            smoothScrollBy(dx, dy,
                    mSmoothScrollByBehavior.configSmoothScrollByInterpolator(dx, dy),
                    mSmoothScrollByBehavior.configSmoothScrollByDuration(dx, dy));
        } else {
            smoothScrollBy(dx, dy, null, UNDEFINED_DURATION);
        }
    }

    @Override
    public void smoothScrollBy(int dx, int dy, @Nullable Interpolator interpolator) {
        if (mSmoothScrollByBehavior != null) {
            smoothScrollBy(dx, dy,
                    interpolator,
                    mSmoothScrollByBehavior.configSmoothScrollByDuration(dx, dy));
        } else {
            smoothScrollBy(dx, dy, interpolator, UNDEFINED_DURATION);
        }
    }

    /**
     * Set factor of how slow the smoothScroller should run. For example when set to 2f, the smooth
     * scroller is twice slower. The value is 1f by default.
     *
     * @param smoothScrollSpeedFactor Factor of how slow the smooth scroll is.
     */
    public final void setSmoothScrollSpeedFactor(float smoothScrollSpeedFactor) {
        mLayoutManager.mSmoothScrollSpeedFactor = smoothScrollSpeedFactor;
    }

    /**
     * @return Factor of how slow the smoothScroller runs. Default value is 1f.
     */
    public final float getSmoothScrollSpeedFactor() {
        return mLayoutManager.mSmoothScrollSpeedFactor;
    }

    /**
     * When holding DPAD, DPAD events are generated faster than the grid view can scroll. The
     * grid view counts unhandled DPAD events and completes the movement after user release DPAD.
     * If the value is set too high, the scrolling will last very long after DPAD is released. If
     * the value is set too low, it may miss many DPAD events. The default value is 10. If app
     * increases {@link #setSmoothScrollSpeedFactor(float)}, it may need decrease the max pending
     * DPAD events to avoid scrolling too long after DPAD release.
     *
     * @param maxPendingMoves Maximum number of pending DPAD events to be remembered.
     */
    public final void setSmoothScrollMaxPendingMoves(int maxPendingMoves) {
        mLayoutManager.mMaxPendingMoves = maxPendingMoves;
    }

    /**
     * When holding DPAD, DPAD events are generated faster than the grid view can scroll. The
     * grid view counts unhandled DPAD events and complete the movement after user release DPAD.
     * If the value is set too high, the scrolling will last very long after DPAD is released. If
     * the value is set too low, it may miss many DPAD events. The default value is 10. If app
     * increases {@link #setSmoothScrollSpeedFactor(float)}, it may need decrease the max pending
     * DPAD events to avoid scrolling too long after DPAD release.
     *
     * @return Maximum number of pending DPAD events to be remembered when smooth scroll cannot
     * catch up speed of DPAD events being sent.
     */
    public final int getSmoothScrollMaxPendingMoves() {
        return mLayoutManager.mMaxPendingMoves;
    }

    /**
     * Sets the number of items to prefetch in
     * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
     * which defines how many inner items should be prefetched when this GridView is nested inside
     * another RecyclerView.
     *
     * <p>Set this value to the number of items this inner GridView will display when it is
     * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items
     * so they are ready, avoiding jank as the inner GridView is scrolled into the viewport.</p>
     *
     * <p>For example, take a VerticalGridView of scrolling HorizontalGridViews. The rows always
     * have 6 items visible in them (or 7 if not aligned). Passing <code>6</code> to this method
     * for each inner GridView will enable RecyclerView's prefetching feature to do create/bind work
     * for 6 views within a row early, before it is scrolled on screen, instead of just the default
     * 4.</p>
     *
     * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView
     * nested in another RecyclerView.</p>
     *
     * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of
     * views that will be visible in this view can incur unnecessary bind work, and an increase to
     * the number of Views created and in active use.</p>
     *
     * @param itemCount Number of items to prefetch
     * @see #getInitialPrefetchItemCount()
     * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
     * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int,
     * RecyclerView.LayoutManager.LayoutPrefetchRegistry)
     */
    public void setInitialPrefetchItemCount(int itemCount) {
        mInitialPrefetchItemCount = itemCount;
    }

    /**
     * Gets the number of items to prefetch in
     * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
     * which defines how many inner items should be prefetched when this GridView is nested inside
     * another RecyclerView.
     *
     * @return number of items to prefetch.
     * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
     * @see #setInitialPrefetchItemCount(int)
     * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int,
     * RecyclerView.LayoutManager.LayoutPrefetchRegistry)
     */
    public int getInitialPrefetchItemCount() {
        return mInitialPrefetchItemCount;
    }

    @Override
    public void removeView(@NonNull View view) {
        boolean retainFocusForChild = view.hasFocus() && isFocusable();
        if (retainFocusForChild) {
            // When animation or scrolling removes a focused child, focus to GridView itself to
            // avoid losing focus.
            mPrivateFlag |= PFLAG_RETAIN_FOCUS_FOR_CHILD;
            requestFocus();
        }
        super.removeView(view);
        if (retainFocusForChild) {
            mPrivateFlag ^= ~PFLAG_RETAIN_FOCUS_FOR_CHILD;
        }
    }

    @Override
    public void removeViewAt(int index) {
        boolean retainFocusForChild = getChildAt(index).hasFocus();
        if (retainFocusForChild) {
            // When animation or scrolling removes a focused child, focus to GridView itself to
            // avoid losing focus.
            mPrivateFlag |= PFLAG_RETAIN_FOCUS_FOR_CHILD;
            requestFocus();
        }
        super.removeViewAt(index);
        if (retainFocusForChild) {
            mPrivateFlag ^= ~PFLAG_RETAIN_FOCUS_FOR_CHILD;
        }
    }
}