| /* |
| * Copyright (C) 2014 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; |
| |
| 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 androidx.annotation.RestrictTo; |
| import androidx.leanback.R; |
| 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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| public final static int FOCUS_SCROLL_ALIGNED = 0; |
| |
| /** |
| * Scroll to make the focused item inside client area. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| public final static 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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| public final static 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 final static 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 final static 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 final static 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 final static int WINDOW_ALIGN_NO_EDGE = 0; |
| |
| /** |
| * Value indicates that percent is not used. |
| */ |
| public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1; |
| |
| /** |
| * Value indicates that percent is not used. |
| */ |
| public final static 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; |
| |
| /** |
| * Listener for intercepting touch dispatch events. |
| */ |
| public interface OnTouchInterceptListener { |
| /** |
| * Returns true if the touch dispatch event should be consumed. |
| */ |
| public boolean onInterceptTouchEvent(MotionEvent event); |
| } |
| |
| /** |
| * Listener for intercepting generic motion dispatch events. |
| */ |
| public interface OnMotionInterceptListener { |
| /** |
| * Returns true if the touch dispatch event should be consumed. |
| */ |
| public boolean onInterceptMotionEvent(MotionEvent event); |
| } |
| |
| /** |
| * Listener for intercepting key dispatch events. |
| */ |
| public interface OnKeyInterceptListener { |
| /** |
| * Returns true if the key dispatch event should be consumed. |
| */ |
| public boolean onInterceptKeyEvent(KeyEvent event); |
| } |
| |
| public interface OnUnhandledKeyListener { |
| /** |
| * Returns true if the key event should be consumed. |
| */ |
| public boolean onUnhandledKey(KeyEvent event); |
| } |
| |
| final GridLayoutManager mLayoutManager; |
| |
| /** |
| * 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; |
| RecyclerView.RecyclerListener mChainedRecyclerListener; |
| private OnUnhandledKeyListener mOnUnhandledKeyListener; |
| |
| /** |
| * Number of items to prefetch when first coming on screen with new data. |
| */ |
| int mInitialPrefetchItemCount = 4; |
| |
| 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.setRecyclerListener(new RecyclerView.RecyclerListener() { |
| @Override |
| public void onViewRecycled(RecyclerView.ViewHolder holder) { |
| mLayoutManager.onChildRecycled(holder); |
| if (mChainedRecyclerListener != null) { |
| mChainedRecyclerListener.onViewRecycled(holder); |
| } |
| } |
| }); |
| } |
| |
| 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> |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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> |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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(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. |
| */ |
| public void setOnChildSelectedListener(OnChildSelectedListener listener) { |
| mLayoutManager.setOnChildSelectedListener(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(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(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(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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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. |
| */ |
| public void setSelectedPositionSmooth(final int position, final ViewHolderTask task) { |
| if (task != null) { |
| RecyclerView.ViewHolder vh = findViewHolderForPosition(position); |
| if (vh == null || hasPendingAdapterUpdates()) { |
| addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { |
| @Override |
| public void onChildViewHolderSelected(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. |
| */ |
| public void setSelectedPosition(final int position, final ViewHolderTask task) { |
| if (task != null) { |
| RecyclerView.ViewHolder vh = findViewHolderForPosition(position); |
| if (vh == null || hasPendingAdapterUpdates()) { |
| addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { |
| @Override |
| public void onChildViewHolderSelectedAndPositioned(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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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 boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { |
| 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(View view, 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 |
| 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, 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(OnTouchInterceptListener listener) { |
| mOnTouchInterceptListener = listener; |
| } |
| |
| /** |
| * Sets the generic motion intercept listener. |
| * @param listener The motion intercept listener. |
| */ |
| public void setOnMotionInterceptListener(OnMotionInterceptListener listener) { |
| mOnMotionInterceptListener = listener; |
| } |
| |
| /** |
| * Sets the key intercept listener. |
| * @param listener The key intercept listener. |
| */ |
| public void setOnKeyInterceptListener(OnKeyInterceptListener listener) { |
| mOnKeyInterceptListener = listener; |
| } |
| |
| /** |
| * Sets the unhandled key listener. |
| * @param listener The unhandled key intercept listener. |
| */ |
| public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) { |
| mOnUnhandledKeyListener = listener; |
| } |
| |
| /** |
| * Returns the unhandled key listener. |
| * @return The unhandled key listener. |
| */ |
| public OnUnhandledKeyListener getOnUnhandledKeyListener() { |
| return mOnUnhandledKeyListener; |
| } |
| |
| @Override |
| public boolean dispatchKeyEvent(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(MotionEvent event) { |
| if (mOnTouchInterceptListener != null) { |
| if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) { |
| return true; |
| } |
| } |
| return super.dispatchTouchEvent(event); |
| } |
| |
| @Override |
| protected boolean dispatchGenericFocusedEvent(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) { |
| mLayoutManager.onRtlPropertiesChanged(layoutDirection); |
| } |
| |
| @Override |
| public void setRecyclerListener(RecyclerView.RecyclerListener listener) { |
| mChainedRecyclerListener = listener; |
| } |
| |
| /** |
| * 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. |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| public void setExtraLayoutSpace(int extraLayoutSpace) { |
| mLayoutManager.setExtraLayoutSpace(extraLayoutSpace); |
| } |
| |
| /** |
| * Returns pixels of extra space for layout child in invisible area. |
| * |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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); |
| } |
| |
| /** |
| * 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. |
| * |
| * @see RecyclerView.LayoutManager#isItemPrefetchEnabled() |
| * @see #setInitialPrefetchItemCount(int) |
| * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry) |
| * |
| * @return number of items to prefetch. |
| */ |
| public int getInitialPrefetchItemCount() { |
| return mInitialPrefetchItemCount; |
| } |
| } |