Gradle dependencies
compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha04'
- groupId: androidx.leanback
- artifactId: leanback
- version: 1.2.0-alpha04
Artifact androidx.leanback:leanback:1.2.0-alpha04 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.leanback:leanback com.android.support:leanback-v17
Androidx class mapping:
androidx.leanback.widget.ListRowPresenter android.support.v17.leanback.widget.ListRowPresenter
Overview
ListRowPresenter renders ListRow using a
 HorizontalGridView hosted in a ListRowView.
 
Hover card
 Optionally, 
ListRowPresenter.setHoverCardPresenterSelector(PresenterSelector) can be used to
 display a view for the currently focused list item below the rendered
 list. This view is known as a hover card.
 
Row selection animation
 ListRowPresenter disables 
RowPresenter's default full row dimming effect and draws
 a dim overlay on each child individually.  A subclass may disable the overlay on each child
 by overriding 
ListRowPresenter.isUsingDefaultListSelectEffect() to return false and write its own child
 dim effect in 
ListRowPresenter.applySelectLevelToChild(ListRowPresenter.ViewHolder, View).
 
Shadow
 ListRowPresenter applies a default shadow to each child view.  Call
 
ListRowPresenter.setShadowEnabled(boolean) to disable shadows.  A subclass may override and return
 false in 
ListRowPresenter.isUsingDefaultShadow() and replace with its own shadow implementation.
Summary
| Constructors | 
|---|
| public | ListRowPresenter() 
 Constructs a ListRowPresenter with defaults. | 
| public | ListRowPresenter(int focusZoomFactor) 
 Constructs a ListRowPresenter with the given parameters. | 
| public | ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) 
 Constructs a ListRowPresenter with the given parameters. | 
| Methods | 
|---|
| protected void | applySelectLevelToChild(ListRowPresenter.ViewHolder rowViewHolder, View childView) 
 Applies select level to a child. | 
| public final boolean | areChildRoundedCornersEnabled() 
 Returns true if rounded corners are enabled for children of this row. | 
| protected abstract RowPresenter.ViewHolder | createRowViewHolder(ViewGroup parent) 
 Called to create a ViewHolder object for a Row. | 
| protected ShadowOverlayHelper.Options | createShadowOverlayOptions() 
 Create ShadowOverlayHelper Options. | 
| protected void | dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) 
 Dispatch item selected event using current selected item in the HorizontalGridView. | 
| public final void | enableChildRoundedCorners(boolean enable) 
 Enables or disabled rounded corners on children of this row. | 
| public void | freeze(RowPresenter.ViewHolder holder, boolean freeze) 
 Freezes/unfreezes the row, typically used when a transition starts/ends. | 
| public int | getExpandedRowHeight() 
 Returns the expanded row height for rows created by this Presenter. | 
| public final int | getFocusZoomFactor() 
 Returns the zoom factor used for focus highlighting. | 
| public final PresenterSelector | getHoverCardPresenterSelector() 
 Returns the PresenterSelector used for showing a select object in a hover card. | 
| public int | getRecycledPoolSize(Presenter presenter) 
 Returns the recycled pool size for the given presenter. | 
| public int | getRowHeight() 
 Returns the row height for list rows created by this Presenter. | 
| public final boolean | getShadowEnabled() 
 Returns true if child shadow is enabled. | 
| public final int | getZoomFactor() 
 Returns the zoom factor used for focus highlighting. | 
| protected void | initializeRowViewHolder(RowPresenter.ViewHolder vh) 
 Called after a RowPresenter.ViewHolder is created for a Row. | 
| public final boolean | isFocusDimmerUsed() 
 Returns true if the focus dimmer is used for focus highlighting; false otherwise. | 
| public final boolean | isKeepChildForeground() 
 Returns true if keeps foreground of child of this row, false otherwise. | 
| public boolean | isUsingDefaultListSelectEffect() 
 Returns true so that default select effect is applied to each individual
 child of HorizontalGridView. | 
| public final boolean | isUsingDefaultSelectEffect() 
 ListRowPresenter overrides the default select effect of RowPresenter
 and return false. | 
| public boolean | isUsingDefaultShadow() 
 Default implementation returns true if SDK version >= 21, shadow (either static or z-order
 based) will be applied to each individual child of HorizontalGridView. | 
| public boolean | isUsingOutlineClipping(Context context) 
 Returns true if leanback view outline is enabled on the system or false otherwise. | 
| public boolean | isUsingZOrder(Context context) 
 Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
 on each child of horizontal list. | 
| protected void | onBindRowViewHolder(RowPresenter.ViewHolder vh, java.lang.Object item) 
 Binds the given row object to the given ViewHolder. | 
| protected void | onRowViewExpanded(RowPresenter.ViewHolder vh, boolean expanded) 
 Called when the row view's expanded state changes. | 
| protected void | onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) 
 Called when the given row view changes selection state. | 
| protected void | onSelectLevelChanged(RowPresenter.ViewHolder holder) 
 Applies select level to header and draws a default color dim over each child
 of HorizontalGridView. | 
| protected void | onUnbindRowViewHolder(RowPresenter.ViewHolder vh) 
 Unbinds the given ViewHolder. | 
| public void | setEntranceTransitionState(RowPresenter.ViewHolder holder, boolean afterEntrance) 
 Changes the visibility of views. | 
| public void | setExpandedRowHeight(int rowHeight) 
 Sets the expanded row height for rows created by this Presenter. | 
| public final void | setHoverCardPresenterSelector(PresenterSelector selector) 
 Sets the PresenterSelector used for showing a select object in a hover card. | 
| public final void | setKeepChildForeground(boolean keep) 
 When ListRowPresenter applies overlay color on the child,  it may change child's foreground
 Drawable. | 
| public void | setNumRows(int numRows) 
 Sets the numbers of rows for rendering the list of items. | 
| public void | setRecycledPoolSize(Presenter presenter, int size) 
 Sets the recycled pool size for the given presenter. | 
| public void | setRowHeight(int rowHeight) 
 Sets the row height for rows created by this Presenter. | 
| public final void | setShadowEnabled(boolean enabled) 
 Enables or disables child shadow. | 
| from RowPresenter | getHeaderPresenter, getRowViewHolder, getSelectEffectEnabled, getSelectLevel, getSyncActivatePolicy, isClippingChildren, onBindViewHolder, onCreateViewHolder, onRowViewAttachedToWindow, onRowViewDetachedFromWindow, onUnbindViewHolder, onViewAttachedToWindow, onViewDetachedFromWindow, setHeaderPresenter, setRowViewExpanded, setRowViewSelected, setSelectEffectEnabled, setSelectLevel, setSyncActivatePolicy | 
| from Presenter | cancelAnimationsRecursive, getFacet, onBindViewHolder, setFacet, setOnClickListener | 
| from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait | 
Constructors
public 
ListRowPresenter()
Constructs a ListRowPresenter with defaults.
 Uses FocusHighlight.ZOOM_FACTOR_MEDIUM for focus zooming and
 disabled dimming on focus.
public 
ListRowPresenter(int focusZoomFactor)
Constructs a ListRowPresenter with the given parameters.
Parameters:
focusZoomFactor: Controls the zoom factor used when an item view is focused. One of
         FocusHighlight.ZOOM_FACTOR_NONE,
         FocusHighlight.ZOOM_FACTOR_SMALL,
         FocusHighlight.ZOOM_FACTOR_XSMALL,
         FocusHighlight.ZOOM_FACTOR_MEDIUM,
         FocusHighlight.ZOOM_FACTOR_LARGE
 Dimming on focus defaults to disabled.
public 
ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer)
Constructs a ListRowPresenter with the given parameters.
Parameters:
focusZoomFactor: Controls the zoom factor used when an item view is focused. One of
         FocusHighlight.ZOOM_FACTOR_NONE,
         FocusHighlight.ZOOM_FACTOR_SMALL,
         FocusHighlight.ZOOM_FACTOR_XSMALL,
         FocusHighlight.ZOOM_FACTOR_MEDIUM,
         FocusHighlight.ZOOM_FACTOR_LARGE
useFocusDimmer: determines if the FocusHighlighter will use the dimmer
Methods
public void 
setRowHeight(int rowHeight)
Sets the row height for rows created by this Presenter. Rows
 created before calling this method will not be updated.
Parameters:
rowHeight: Row height in pixels, or WRAP_CONTENT, or 0
 to use the default height.
public int 
getRowHeight()
Returns the row height for list rows created by this Presenter.
public void 
setExpandedRowHeight(int rowHeight)
Sets the expanded row height for rows created by this Presenter.
 If not set, expanded rows have the same height as unexpanded
 rows.
Parameters:
rowHeight: The row height in to use when the row is expanded,
        in pixels, or WRAP_CONTENT, or 0 to use the default.
public int 
getExpandedRowHeight()
Returns the expanded row height for rows created by this Presenter.
public final int 
getFocusZoomFactor()
Returns the zoom factor used for focus highlighting.
public final int 
getZoomFactor()
Deprecated: use ListRowPresenter.getFocusZoomFactor() instead.
Returns the zoom factor used for focus highlighting.
public final boolean 
isFocusDimmerUsed()
Returns true if the focus dimmer is used for focus highlighting; false otherwise.
public void 
setNumRows(int numRows)
Sets the numbers of rows for rendering the list of items. By default, it is
 set to 1.
Called after a RowPresenter.ViewHolder is created for a Row.
 Subclasses may override this method and start by calling
 super.initializeRowViewHolder(ViewHolder).
Parameters:
vh: The ViewHolder to initialize for the Row.
public void 
setRecycledPoolSize(
Presenter presenter, int size)
Sets the recycled pool size for the given presenter.
public int 
getRecycledPoolSize(
Presenter presenter)
Returns the recycled pool size for the given presenter.
Sets the PresenterSelector used for showing a select object in a hover card.
Returns the PresenterSelector used for showing a select object in a hover card.
Called to create a ViewHolder object for a Row. Subclasses will override
 this method to return a different concrete ViewHolder object.
Parameters:
parent: The parent View for the Row's view holder.
Returns:
A ViewHolder for the Row's View.
Dispatch item selected event using current selected item in the HorizontalGridView.
 The method should only be called from onRowViewSelected().
Called when the given row view changes selection state.  A subclass may override this to
 respond to selected state changes of a Row.  A subclass may make visual changes to Row view
 but must not create animation on the Row view.
Called when the row view's expanded state changes.  A subclass may override this method to
 respond to expanded state changes of a Row.
 The default implementation will hide/show the header view. Subclasses may
 make visual changes to the Row View but must not create animation on the
 Row view.
Binds the given row object to the given ViewHolder.
 Derived classes of RowPresenter overriding
 RowPresenter.onBindRowViewHolder(RowPresenter.ViewHolder, Object) must call through the super class's
 implementation of this method.
Unbinds the given ViewHolder.
 Derived classes of RowPresenter overriding RowPresenter.onUnbindRowViewHolder(RowPresenter.ViewHolder)
 must call through the super class's implementation of this method.
public final boolean 
isUsingDefaultSelectEffect()
ListRowPresenter overrides the default select effect of RowPresenter
 and return false.
public boolean 
isUsingDefaultListSelectEffect()
Returns true so that default select effect is applied to each individual
 child of HorizontalGridView.  Subclass may return false to disable
 the default implementation and implement ListRowPresenter.applySelectLevelToChild(ListRowPresenter.ViewHolder, View).
See also: ListRowPresenter.applySelectLevelToChild(ListRowPresenter.ViewHolder, View), ListRowPresenter.onSelectLevelChanged(RowPresenter.ViewHolder)
public boolean 
isUsingDefaultShadow()
Default implementation returns true if SDK version >= 21, shadow (either static or z-order
 based) will be applied to each individual child of HorizontalGridView.
 Subclass may return false to disable default implementation of shadow and provide its own.
public boolean 
isUsingZOrder(Context context)
Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
 on each child of horizontal list.   If subclass returns false in isUsingDefaultShadow()
 and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
public boolean 
isUsingOutlineClipping(Context context)
Returns true if leanback view outline is enabled on the system or false otherwise. When
 false, rounded corner will not be enabled even ListRowPresenter.enableChildRoundedCorners(boolean)
 is called with true.
Parameters:
context: Context to retrieve system settings.
Returns:
True if leanback view outline is enabled on the system or false otherwise.
public final void 
setShadowEnabled(boolean enabled)
Enables or disables child shadow.
 This is not only for enable/disable default shadow implementation but also subclass must
 respect this flag.
public final boolean 
getShadowEnabled()
Returns true if child shadow is enabled.
 This is not only for enable/disable default shadow implementation but also subclass must
 respect this flag.
public final void 
enableChildRoundedCorners(boolean enable)
Enables or disabled rounded corners on children of this row.
 Supported on Android SDK >= L.
public final boolean 
areChildRoundedCornersEnabled()
Returns true if rounded corners are enabled for children of this row.
public final void 
setKeepChildForeground(boolean keep)
When ListRowPresenter applies overlay color on the child,  it may change child's foreground
 Drawable.  If application uses child's foreground for other purposes such as ripple effect,
 it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
Parameters:
keep: true if keep foreground of child of this row, false ListRowPresenter might change
             the foreground of the child.
public final boolean 
isKeepChildForeground()
Returns true if keeps foreground of child of this row, false otherwise.  When
 ListRowPresenter applies overlay color on the child,  it may change child's foreground
 Drawable.  If application uses child's foreground for other purposes such as ripple effect,
 it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
Returns:
true if keeps foreground of child of this row, false otherwise.
Create ShadowOverlayHelper Options.  Subclass may override.
 e.g.
 
 return new ShadowOverlayHelper.Options().roundedCornerRadius(10);
 
Returns:
The options to be used for shadow, overlay and rounded corner.
Applies select level to header and draws a default color dim over each child
 of HorizontalGridView.
 
 Subclass may override this method and starts with calling super if it has views to apply
 select effect other than header and HorizontalGridView.
 To override the default color dim over each child of HorizontalGridView,
 app should override ListRowPresenter.isUsingDefaultListSelectEffect() to
 return false and override ListRowPresenter.applySelectLevelToChild(ListRowPresenter.ViewHolder, View).
 
See also: ListRowPresenter.isUsingDefaultListSelectEffect(), RowPresenter.ViewHolder.getSelectLevel(), ListRowPresenter.applySelectLevelToChild(ListRowPresenter.ViewHolder, View)
Applies select level to a child.  Default implementation draws a default color
 dim over each child of HorizontalGridView. This method is called on all children in
 ListRowPresenter.onSelectLevelChanged(RowPresenter.ViewHolder) and when a child is attached to
 HorizontalGridView.
 
 Subclass may disable the default implementation by override
 ListRowPresenter.isUsingDefaultListSelectEffect() to return false and deal with the individual item
 select level by itself.
 
Parameters:
rowViewHolder: The ViewHolder of the Row
childView: The child of HorizontalGridView to apply select level.
See also: ListRowPresenter.isUsingDefaultListSelectEffect(), RowPresenter.ViewHolder.getSelectLevel(), ListRowPresenter.onSelectLevelChanged(RowPresenter.ViewHolder)
Freezes/unfreezes the row, typically used when a transition starts/ends.
 This method is called by the fragment, it should not call it directly by the application.
Changes the visibility of views.  The entrance transition will be run against the views that
 change visibilities.  A subclass may override and begin with calling
 super.setEntranceTransitionState().  This method is called by the fragment,
 it should not be called directly by the application.
Parameters:
holder: The ViewHolder of the row.
afterEntrance: true if children of row participating in entrance transition
                       should be set to visible, false otherwise.
Source
/*
 * 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 android.content.Context;
import android.content.res.TypedArray;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.leanback.R;
import androidx.leanback.system.Settings;
import androidx.leanback.transition.TransitionHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashMap;
/**
 * ListRowPresenter renders {@link ListRow} using a
 * {@link HorizontalGridView} hosted in a {@link ListRowView}.
 *
 * <h3>Hover card</h3>
 * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to
 * display a view for the currently focused list item below the rendered
 * list. This view is known as a hover card.
 *
 * <h3>Row selection animation</h3>
 * ListRowPresenter disables {@link RowPresenter}'s default full row dimming effect and draws
 * a dim overlay on each child individually.  A subclass may disable the overlay on each child
 * by overriding {@link #isUsingDefaultListSelectEffect()} to return false and write its own child
 * dim effect in {@link #applySelectLevelToChild(ViewHolder, View)}.
 *
 * <h3>Shadow</h3>
 * ListRowPresenter applies a default shadow to each child view.  Call
 * {@link #setShadowEnabled(boolean)} to disable shadows.  A subclass may override and return
 * false in {@link #isUsingDefaultShadow()} and replace with its own shadow implementation.
 */
public class ListRowPresenter extends RowPresenter {
    private static final String TAG = "ListRowPresenter";
    private static final boolean DEBUG = false;
    private static final int DEFAULT_RECYCLED_POOL_SIZE = 24;
    /**
     * ViewHolder for the ListRowPresenter.
     */
    public static class ViewHolder extends RowPresenter.ViewHolder {
        final ListRowPresenter mListRowPresenter;
        final HorizontalGridView mGridView;
        ItemBridgeAdapter mItemBridgeAdapter;
        final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
        final int mPaddingTop;
        final int mPaddingBottom;
        final int mPaddingLeft;
        final int mPaddingRight;
        public ViewHolder(
                @NonNull View rootView,
                @NonNull HorizontalGridView gridView,
                @NonNull ListRowPresenter p
        ) {
            super(rootView);
            mGridView = gridView;
            mListRowPresenter = p;
            mPaddingTop = mGridView.getPaddingTop();
            mPaddingBottom = mGridView.getPaddingBottom();
            mPaddingLeft = mGridView.getPaddingLeft();
            mPaddingRight = mGridView.getPaddingRight();
        }
        /**
         * Gets ListRowPresenter that creates this ViewHolder.
         * @return ListRowPresenter that creates this ViewHolder.
         */
        @NonNull
        public final ListRowPresenter getListRowPresenter() {
            return mListRowPresenter;
        }
        /**
         * Gets HorizontalGridView that shows a list of items.
         * @return HorizontalGridView that shows a list of items.
         */
        @NonNull
        public final HorizontalGridView getGridView() {
            return mGridView;
        }
        /**
         * Gets ItemBridgeAdapter that creates the list of items.
         * @return ItemBridgeAdapter that creates the list of items.
         */
        @NonNull
        public final ItemBridgeAdapter getBridgeAdapter() {
            return mItemBridgeAdapter;
        }
        /**
         * Gets selected item position in adapter.
         * @return Selected item position in adapter.
         */
        public int getSelectedPosition() {
            return mGridView.getSelectedPosition();
        }
        /**
         * Gets ViewHolder at a position in adapter.  Returns null if the item does not exist
         * or the item is not bound to a view.
         * @param position Position of the item in adapter.
         * @return ViewHolder bounds to the item.
         */
        @Nullable
        public Presenter.ViewHolder getItemViewHolder(int position) {
            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
                    .findViewHolderForAdapterPosition(position);
            if (ibvh == null) {
                return null;
            }
            return ibvh.getViewHolder();
        }
        @Nullable
        @Override
        public Presenter.ViewHolder getSelectedItemViewHolder() {
            return getItemViewHolder(getSelectedPosition());
        }
        @Nullable
        @Override
        public Object getSelectedItem() {
            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
                    .findViewHolderForAdapterPosition(getSelectedPosition());
            if (ibvh == null) {
                return null;
            }
            return ibvh.getItem();
        }
    }
    /**
     * A task on the ListRowPresenter.ViewHolder that can select an item by position in the
     * HorizontalGridView and perform an optional item task on it.
     */
    public static class SelectItemViewHolderTask extends Presenter.ViewHolderTask {
        private int mItemPosition;
        private boolean mSmoothScroll = true;
        Presenter.ViewHolderTask mItemTask;
        public SelectItemViewHolderTask(int itemPosition) {
            setItemPosition(itemPosition);
        }
        /**
         * Sets the adapter position of item to select.
         * @param itemPosition Position of the item in adapter.
         */
        public void setItemPosition(int itemPosition) {
            mItemPosition = itemPosition;
        }
        /**
         * Returns the adapter position of item to select.
         * @return The adapter position of item to select.
         */
        public int getItemPosition() {
            return mItemPosition;
        }
        /**
         * Sets smooth scrolling to the item or jump to the item without scrolling.  By default it is
         * true.
         * @param smoothScroll True for smooth scrolling to the item, false otherwise.
         */
        public void setSmoothScroll(boolean smoothScroll) {
            mSmoothScroll = smoothScroll;
        }
        /**
         * Returns true if smooth scrolling to the item false otherwise.  By default it is true.
         * @return True for smooth scrolling to the item, false otherwise.
         */
        public boolean isSmoothScroll() {
            return mSmoothScroll;
        }
        /**
         * Returns optional task to run when the item is selected, null for no task.
         * @return Optional task to run when the item is selected, null for no task.
         */
        @Nullable
        public Presenter.ViewHolderTask getItemTask() {
            return mItemTask;
        }
        /**
         * Sets task to run when the item is selected, null for no task.
         * @param itemTask Optional task to run when the item is selected, null for no task.
         */
        public void setItemTask(@Nullable ViewHolderTask itemTask) {
            mItemTask = itemTask;
        }
        @Override
        public void run(@Nullable Presenter.ViewHolder holder) {
            if (holder instanceof ListRowPresenter.ViewHolder) {
                HorizontalGridView gridView = ((ListRowPresenter.ViewHolder) holder).getGridView();
                androidx.leanback.widget.ViewHolderTask task = null;
                if (mItemTask != null) {
                    task = new androidx.leanback.widget.ViewHolderTask() {
                        final Presenter.ViewHolderTask itemTask = mItemTask;
                        @Override
                        public void run(@NonNull RecyclerView.ViewHolder rvh) {
                            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) rvh;
                            itemTask.run(ibvh.getViewHolder());
                        }
                    };
                }
                if (isSmoothScroll()) {
                    gridView.setSelectedPositionSmooth(mItemPosition, task);
                } else {
                    gridView.setSelectedPosition(mItemPosition, task);
                }
            }
        }
    }
    class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter {
        ListRowPresenter.ViewHolder mRowViewHolder;
        ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) {
            mRowViewHolder = rowViewHolder;
        }
        @Override
        protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
            if (viewHolder.itemView instanceof ViewGroup) {
                TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true);
            }
            if (mShadowOverlayHelper != null) {
                mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
            }
        }
        @Override
        @SuppressWarnings("unchecked")
        public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
            // Only when having an OnItemClickListener, we will attach the OnClickListener.
            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
                                mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView);
                        if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                            mRowViewHolder.getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
                                    ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow);
                        }
                    }
                });
            }
        }
        @Override
        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                viewHolder.mHolder.view.setOnClickListener(null);
            }
        }
        @Override
        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
            applySelectLevelToChild(mRowViewHolder, viewHolder.itemView);
            mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
        }
        @Override
        public void onAddPresenter(Presenter presenter, int type) {
            mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
                    type, getRecycledPoolSize(presenter));
        }
    }
    private int mNumRows = 1;
    private int mRowHeight;
    private int mExpandedRowHeight;
    private PresenterSelector mHoverCardPresenterSelector;
    private int mFocusZoomFactor;
    private boolean mUseFocusDimmer;
    private boolean mShadowEnabled = true;
    private int mBrowseRowsFadingEdgeLength = -1;
    private boolean mRoundedCornersEnabled = true;
    private boolean mKeepChildForeground = true;
    private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>();
    ShadowOverlayHelper mShadowOverlayHelper;
    private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper;
    private static int sSelectedRowTopPadding;
    private static int sExpandedSelectedRowTopPadding;
    private static int sExpandedRowNoHovercardBottomPadding;
    /**
     * Constructs a ListRowPresenter with defaults.
     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
     * disabled dimming on focus.
     */
    public ListRowPresenter() {
        this(FocusHighlight.ZOOM_FACTOR_MEDIUM);
    }
    /**
     * Constructs a ListRowPresenter with the given parameters.
     *
     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
     * Dimming on focus defaults to disabled.
     */
    public ListRowPresenter(int focusZoomFactor) {
        this(focusZoomFactor, false);
    }
    /**
     * Constructs a ListRowPresenter with the given parameters.
     *
     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
     */
    public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) {
        if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) {
            throw new IllegalArgumentException("Unhandled zoom factor");
        }
        mFocusZoomFactor = focusZoomFactor;
        mUseFocusDimmer = useFocusDimmer;
    }
    /**
     * Sets the row height for rows created by this Presenter. Rows
     * created before calling this method will not be updated.
     *
     * @param rowHeight Row height in pixels, or WRAP_CONTENT, or 0
     * to use the default height.
     */
    public void setRowHeight(int rowHeight) {
        mRowHeight = rowHeight;
    }
    /**
     * Returns the row height for list rows created by this Presenter.
     */
    public int getRowHeight() {
        return mRowHeight;
    }
    /**
     * Sets the expanded row height for rows created by this Presenter.
     * If not set, expanded rows have the same height as unexpanded
     * rows.
     *
     * @param rowHeight The row height in to use when the row is expanded,
     *        in pixels, or WRAP_CONTENT, or 0 to use the default.
     */
    public void setExpandedRowHeight(int rowHeight) {
        mExpandedRowHeight = rowHeight;
    }
    /**
     * Returns the expanded row height for rows created by this Presenter.
     */
    public int getExpandedRowHeight() {
        return mExpandedRowHeight != 0 ? mExpandedRowHeight : mRowHeight;
    }
    /**
     * Returns the zoom factor used for focus highlighting.
     */
    public final int getFocusZoomFactor() {
        return mFocusZoomFactor;
    }
    /**
     * Returns the zoom factor used for focus highlighting.
     * @deprecated use {@link #getFocusZoomFactor} instead.
     */
    @Deprecated
    public final int getZoomFactor() {
        return mFocusZoomFactor;
    }
    /**
     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
     */
    public final boolean isFocusDimmerUsed() {
        return mUseFocusDimmer;
    }
    /**
     * Sets the numbers of rows for rendering the list of items. By default, it is
     * set to 1.
     */
    public void setNumRows(int numRows) {
        this.mNumRows = numRows;
    }
    @Override
    protected void initializeRowViewHolder(RowPresenter.ViewHolder vh) {
        super.initializeRowViewHolder(vh);
        final ViewHolder rowViewHolder = (ViewHolder) vh;
        Context context = vh.view.getContext();
        if (mShadowOverlayHelper == null) {
            mShadowOverlayHelper = new ShadowOverlayHelper.Builder()
                    .needsOverlay(needsDefaultListSelectEffect())
                    .needsShadow(needsDefaultShadow())
                    .needsRoundedCorner(isUsingOutlineClipping(context)
                            && areChildRoundedCornersEnabled())
                    .preferZOrder(isUsingZOrder(context))
                    .keepForegroundDrawable(mKeepChildForeground)
                    .options(createShadowOverlayOptions())
                    .build(context);
            if (mShadowOverlayHelper.needsWrapper()) {
                mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper(
                        mShadowOverlayHelper);
            }
        }
        rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
        // set wrapper if needed
        rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper);
        mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView);
        FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
                mFocusZoomFactor, mUseFocusDimmer);
        rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
                != ShadowOverlayHelper.SHADOW_DYNAMIC);
        rowViewHolder.mGridView.setOnChildSelectedListener(
                new OnChildSelectedListener() {
                    @Override
                    public void onChildSelected(
                            @NonNull ViewGroup parent,
                            @Nullable View view,
                            int position,
                            long id
                    ) {
                        selectChildView(rowViewHolder, view, true);
                    }
                });
        rowViewHolder.mGridView.setOnUnhandledKeyListener(
                new BaseGridView.OnUnhandledKeyListener() {
                @Override
                public boolean onUnhandledKey(KeyEvent event) {
                    return rowViewHolder.getOnKeyListener() != null
                            && rowViewHolder.getOnKeyListener().onKey(
                                    rowViewHolder.view, event.getKeyCode(), event);
                }
            });
        rowViewHolder.mGridView.setNumRows(mNumRows);
    }
    final boolean needsDefaultListSelectEffect() {
        return isUsingDefaultListSelectEffect() && getSelectEffectEnabled();
    }
    /**
     * Sets the recycled pool size for the given presenter.
     */
    public void setRecycledPoolSize(Presenter presenter, int size) {
        mRecycledPoolSize.put(presenter, size);
    }
    /**
     * Returns the recycled pool size for the given presenter.
     */
    public int getRecycledPoolSize(Presenter presenter) {
        return mRecycledPoolSize.containsKey(presenter) ? mRecycledPoolSize.get(presenter) :
                DEFAULT_RECYCLED_POOL_SIZE;
    }
    /**
     * Sets the {@link PresenterSelector} used for showing a select object in a hover card.
     */
    public final void setHoverCardPresenterSelector(PresenterSelector selector) {
        mHoverCardPresenterSelector = selector;
    }
    /**
     * Returns the {@link PresenterSelector} used for showing a select object in a hover card.
     */
    public final PresenterSelector getHoverCardPresenterSelector() {
        return mHoverCardPresenterSelector;
    }
    /*
     * Perform operations when a child of horizontal grid view is selected.
     */
    @SuppressWarnings("unchecked")
    void selectChildView(ViewHolder rowViewHolder, View view, boolean fireEvent) {
        if (view != null) {
            if (rowViewHolder.mSelected) {
                ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
                        rowViewHolder.mGridView.getChildViewHolder(view);
                if (mHoverCardPresenterSelector != null) {
                    rowViewHolder.mHoverCardViewSwitcher.select(
                            rowViewHolder.mGridView, view, ibh.mItem);
                }
                if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) {
                    rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
                            ibh.mHolder, ibh.mItem, rowViewHolder, rowViewHolder.mRow);
                }
            }
        } else {
            if (mHoverCardPresenterSelector != null) {
                rowViewHolder.mHoverCardViewSwitcher.unselect();
            }
            if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) {
                rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
                        null, null, rowViewHolder, rowViewHolder.mRow);
            }
        }
    }
    private static void initStatics(Context context) {
        if (sSelectedRowTopPadding == 0) {
            sSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
                    R.dimen.lb_browse_selected_row_top_padding);
            sExpandedSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
                    R.dimen.lb_browse_expanded_selected_row_top_padding);
            sExpandedRowNoHovercardBottomPadding = context.getResources().getDimensionPixelSize(
                    R.dimen.lb_browse_expanded_row_no_hovercard_bottom_padding);
        }
    }
    private int getSpaceUnderBaseline(ListRowPresenter.ViewHolder vh) {
        RowHeaderPresenter.ViewHolder headerViewHolder = vh.getHeaderViewHolder();
        if (headerViewHolder != null) {
            if (getHeaderPresenter() != null) {
                return getHeaderPresenter().getSpaceUnderBaseline(headerViewHolder);
            }
            return headerViewHolder.view.getPaddingBottom();
        }
        return 0;
    }
    private void setVerticalPadding(ListRowPresenter.ViewHolder vh) {
        int paddingTop, paddingBottom;
        // Note: sufficient bottom padding needed for card shadows.
        if (vh.isExpanded()) {
            int headerSpaceUnderBaseline = getSpaceUnderBaseline(vh);
            if (DEBUG) Log.v(TAG, "headerSpaceUnderBaseline " + headerSpaceUnderBaseline);
            paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop)
                    - headerSpaceUnderBaseline;
            paddingBottom = mHoverCardPresenterSelector == null
                    ? sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom;
        } else if (vh.isSelected()) {
            paddingTop = sSelectedRowTopPadding - vh.mPaddingBottom;
            paddingBottom = sSelectedRowTopPadding;
        } else {
            paddingTop = 0;
            paddingBottom = vh.mPaddingBottom;
        }
        vh.getGridView().setPadding(vh.mPaddingLeft, paddingTop, vh.mPaddingRight,
                paddingBottom);
    }
    @Override
    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
        initStatics(parent.getContext());
        ListRowView rowView = new ListRowView(parent.getContext());
        setupFadingEffect(rowView);
        if (mRowHeight != 0) {
            rowView.getGridView().setRowHeight(mRowHeight);
        }
        return new ViewHolder(rowView, rowView.getGridView(), this);
    }
    /**
     * Dispatch item selected event using current selected item in the {@link HorizontalGridView}.
     * The method should only be called from onRowViewSelected().
     */
    @Override
    @SuppressWarnings("unchecked")
    protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) {
        ViewHolder vh = (ViewHolder)holder;
        ItemBridgeAdapter.ViewHolder itemViewHolder = (ItemBridgeAdapter.ViewHolder)
                vh.mGridView.findViewHolderForPosition(vh.mGridView.getSelectedPosition());
        if (itemViewHolder == null) {
            super.dispatchItemSelectedListener(holder, selected);
            return;
        }
        if (selected) {
            if (holder.getOnItemViewSelectedListener() != null) {
                holder.getOnItemViewSelectedListener().onItemSelected(
                        itemViewHolder.getViewHolder(), itemViewHolder.mItem, vh, vh.getRow());
            }
        }
    }
    @Override
    protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
        super.onRowViewSelected(holder, selected);
        ViewHolder vh = (ViewHolder) holder;
        setVerticalPadding(vh);
        updateFooterViewSwitcher(vh);
    }
    /*
     * Show or hide hover card when row selection or expanded state is changed.
     */
    private void updateFooterViewSwitcher(ViewHolder vh) {
        if (vh.mExpanded && vh.mSelected) {
            if (mHoverCardPresenterSelector != null) {
                vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view,
                        mHoverCardPresenterSelector);
            }
            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
                    vh.mGridView.findViewHolderForPosition(
                            vh.mGridView.getSelectedPosition());
            selectChildView(vh, ibh == null ? null : ibh.itemView, false);
        } else {
            if (mHoverCardPresenterSelector != null) {
                vh.mHoverCardViewSwitcher.unselect();
            }
        }
    }
    private void setupFadingEffect(ListRowView rowView) {
        // content is completely faded at 1/2 padding of left, fading length is 1/2 of padding.
        HorizontalGridView gridView = rowView.getGridView();
        if (mBrowseRowsFadingEdgeLength < 0) {
            TypedArray ta = gridView.getContext()
                    .obtainStyledAttributes(R.styleable.LeanbackTheme);
            mBrowseRowsFadingEdgeLength = (int) ta.getDimension(
                    R.styleable.LeanbackTheme_browseRowsFadingEdgeLength, 0);
            ta.recycle();
        }
        gridView.setFadingLeftEdgeLength(mBrowseRowsFadingEdgeLength);
    }
    @Override
    protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) {
        super.onRowViewExpanded(holder, expanded);
        ViewHolder vh = (ViewHolder) holder;
        if (getRowHeight() != getExpandedRowHeight()) {
            int newHeight = expanded ? getExpandedRowHeight() : getRowHeight();
            vh.getGridView().setRowHeight(newHeight);
        }
        setVerticalPadding(vh);
        updateFooterViewSwitcher(vh);
    }
    @Override
    protected void onBindRowViewHolder(
            @NonNull RowPresenter.ViewHolder holder,
            @NonNull Object item
    ) {
        super.onBindRowViewHolder(holder, item);
        ViewHolder vh = (ViewHolder) holder;
        ListRow rowItem = (ListRow) item;
        vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
        vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
        vh.mGridView.setContentDescription(rowItem.getContentDescription());
    }
    @Override
    protected void onUnbindRowViewHolder(@NonNull RowPresenter.ViewHolder holder) {
        ViewHolder vh = (ViewHolder) holder;
        vh.mGridView.setAdapter(null);
        vh.mItemBridgeAdapter.clear();
        super.onUnbindRowViewHolder(holder);
    }
    /**
     * ListRowPresenter overrides the default select effect of {@link RowPresenter}
     * and return false.
     */
    @Override
    public final boolean isUsingDefaultSelectEffect() {
        return false;
    }
    /**
     * Returns true so that default select effect is applied to each individual
     * child of {@link HorizontalGridView}.  Subclass may return false to disable
     * the default implementation and implement {@link #applySelectLevelToChild(ViewHolder, View)}.
     * @see #applySelectLevelToChild(ViewHolder, View)
     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
     */
    public boolean isUsingDefaultListSelectEffect() {
        return true;
    }
    /**
     * Default implementation returns true if SDK version >= 21, shadow (either static or z-order
     * based) will be applied to each individual child of {@link HorizontalGridView}.
     * Subclass may return false to disable default implementation of shadow and provide its own.
     */
    public boolean isUsingDefaultShadow() {
        return ShadowOverlayHelper.supportsShadow();
    }
    /**
     * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
     * on each child of horizontal list.   If subclass returns false in isUsingDefaultShadow()
     * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
     */
    public boolean isUsingZOrder(Context context) {
        return !Settings.getInstance(context).preferStaticShadows();
    }
    /**
     * Returns true if leanback view outline is enabled on the system or false otherwise. When
     * false, rounded corner will not be enabled even {@link #enableChildRoundedCorners(boolean)}
     * is called with true.
     *
     * @param context Context to retrieve system settings.
     * @return True if leanback view outline is enabled on the system or false otherwise.
     */
    public boolean isUsingOutlineClipping(Context context) {
        return !Settings.getInstance(context).isOutlineClippingDisabled();
    }
    /**
     * Enables or disables child shadow.
     * This is not only for enable/disable default shadow implementation but also subclass must
     * respect this flag.
     */
    public final void setShadowEnabled(boolean enabled) {
        mShadowEnabled = enabled;
    }
    /**
     * Returns true if child shadow is enabled.
     * This is not only for enable/disable default shadow implementation but also subclass must
     * respect this flag.
     */
    public final boolean getShadowEnabled() {
        return mShadowEnabled;
    }
    /**
     * Enables or disabled rounded corners on children of this row.
     * Supported on Android SDK >= L.
     */
    public final void enableChildRoundedCorners(boolean enable) {
        mRoundedCornersEnabled = enable;
    }
    /**
     * Returns true if rounded corners are enabled for children of this row.
     */
    public final boolean areChildRoundedCornersEnabled() {
        return mRoundedCornersEnabled;
    }
    final boolean needsDefaultShadow() {
        return isUsingDefaultShadow() && getShadowEnabled();
    }
    /**
     * When ListRowPresenter applies overlay color on the child,  it may change child's foreground
     * Drawable.  If application uses child's foreground for other purposes such as ripple effect,
     * it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
     *
     * @param keep true if keep foreground of child of this row, false ListRowPresenter might change
     *             the foreground of the child.
     */
    public final void setKeepChildForeground(boolean keep) {
        mKeepChildForeground = keep;
    }
    /**
     * Returns true if keeps foreground of child of this row, false otherwise.  When
     * ListRowPresenter applies overlay color on the child,  it may change child's foreground
     * Drawable.  If application uses child's foreground for other purposes such as ripple effect,
     * it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
     *
     * @return true if keeps foreground of child of this row, false otherwise.
     */
    public final boolean isKeepChildForeground() {
        return mKeepChildForeground;
    }
    /**
     * Create ShadowOverlayHelper Options.  Subclass may override.
     * e.g.
     * <code>
     * return new ShadowOverlayHelper.Options().roundedCornerRadius(10);
     * </code>
     *
     * @return The options to be used for shadow, overlay and rounded corner.
     */
    protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
        return ShadowOverlayHelper.Options.DEFAULT;
    }
    /**
     * Applies select level to header and draws a default color dim over each child
     * of {@link HorizontalGridView}.
     * <p>
     * Subclass may override this method and starts with calling super if it has views to apply
     * select effect other than header and HorizontalGridView.
     * To override the default color dim over each child of {@link HorizontalGridView},
     * app should override {@link #isUsingDefaultListSelectEffect()} to
     * return false and override {@link #applySelectLevelToChild(ViewHolder, View)}.
     * </p>
     * @see #isUsingDefaultListSelectEffect()
     * @see RowPresenter.ViewHolder#getSelectLevel()
     * @see #applySelectLevelToChild(ViewHolder, View)
     */
    @Override
    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
        super.onSelectLevelChanged(holder);
        ViewHolder vh = (ViewHolder) holder;
        for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
            applySelectLevelToChild(vh, vh.mGridView.getChildAt(i));
        }
    }
    /**
     * Applies select level to a child.  Default implementation draws a default color
     * dim over each child of {@link HorizontalGridView}. This method is called on all children in
     * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)} and when a child is attached to
     * {@link HorizontalGridView}.
     * <p>
     * Subclass may disable the default implementation by override
     * {@link #isUsingDefaultListSelectEffect()} to return false and deal with the individual item
     * select level by itself.
     * </p>
     * @param rowViewHolder The ViewHolder of the Row
     * @param childView The child of {@link HorizontalGridView} to apply select level.
     *
     * @see #isUsingDefaultListSelectEffect()
     * @see RowPresenter.ViewHolder#getSelectLevel()
     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
     */
    protected void applySelectLevelToChild(ViewHolder rowViewHolder, View childView) {
        if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) {
            int dimmedColor = rowViewHolder.mColorDimmer.getPaint().getColor();
            mShadowOverlayHelper.setOverlayColor(childView, dimmedColor);
        }
    }
    @Override
    public void freeze(@NonNull RowPresenter.ViewHolder holder, boolean freeze) {
        ViewHolder vh = (ViewHolder) holder;
        vh.mGridView.setScrollEnabled(!freeze);
        vh.mGridView.setAnimateChildLayout(!freeze);
    }
    @Override
    public void setEntranceTransitionState(@NonNull RowPresenter.ViewHolder holder,
            boolean afterEntrance) {
        super.setEntranceTransitionState(holder, afterEntrance);
        ((ViewHolder) holder).mGridView.setChildrenVisibility(
                afterEntrance? View.VISIBLE : View.INVISIBLE);
    }
}