public class

ListRowPresenter

extends RowPresenter

 java.lang.Object

androidx.leanback.widget.Presenter

androidx.leanback.widget.RowPresenter

↳androidx.leanback.widget.ListRowPresenter

Gradle dependencies

compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha02'

  • groupId: androidx.leanback
  • artifactId: leanback
  • version: 1.2.0-alpha02

Artifact androidx.leanback:leanback:1.2.0-alpha02 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

Fields
from RowPresenterSYNC_ACTIVATED_CUSTOM, SYNC_ACTIVATED_TO_EXPANDED, SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED, SYNC_ACTIVATED_TO_SELECTED
Constructors
publicListRowPresenter()

Constructs a ListRowPresenter with defaults.

publicListRowPresenter(int focusZoomFactor)

Constructs a ListRowPresenter with the given parameters.

publicListRowPresenter(int focusZoomFactor, boolean useFocusDimmer)

Constructs a ListRowPresenter with the given parameters.

Methods
protected voidapplySelectLevelToChild(ListRowPresenter.ViewHolder rowViewHolder, View childView)

Applies select level to a child.

public final booleanareChildRoundedCornersEnabled()

Returns true if rounded corners are enabled for children of this row.

protected abstract RowPresenter.ViewHoldercreateRowViewHolder(ViewGroup parent)

Called to create a ViewHolder object for a Row.

protected ShadowOverlayHelper.OptionscreateShadowOverlayOptions()

Create ShadowOverlayHelper Options.

protected voiddispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected)

Dispatch item selected event using current selected item in the HorizontalGridView.

public final voidenableChildRoundedCorners(boolean enable)

Enables or disabled rounded corners on children of this row.

public voidfreeze(RowPresenter.ViewHolder holder, boolean freeze)

Freezes/unfreezes the row, typically used when a transition starts/ends.

public intgetExpandedRowHeight()

Returns the expanded row height for rows created by this Presenter.

public final intgetFocusZoomFactor()

Returns the zoom factor used for focus highlighting.

public final PresenterSelectorgetHoverCardPresenterSelector()

Returns the PresenterSelector used for showing a select object in a hover card.

public intgetRecycledPoolSize(Presenter presenter)

Returns the recycled pool size for the given presenter.

public intgetRowHeight()

Returns the row height for list rows created by this Presenter.

public final booleangetShadowEnabled()

Returns true if child shadow is enabled.

public final intgetZoomFactor()

Returns the zoom factor used for focus highlighting.

protected voidinitializeRowViewHolder(RowPresenter.ViewHolder vh)

Called after a RowPresenter.ViewHolder is created for a Row.

public final booleanisFocusDimmerUsed()

Returns true if the focus dimmer is used for focus highlighting; false otherwise.

public final booleanisKeepChildForeground()

Returns true if keeps foreground of child of this row, false otherwise.

public booleanisUsingDefaultListSelectEffect()

Returns true so that default select effect is applied to each individual child of HorizontalGridView.

public final booleanisUsingDefaultSelectEffect()

ListRowPresenter overrides the default select effect of RowPresenter and return false.

public booleanisUsingDefaultShadow()

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 booleanisUsingOutlineClipping(Context context)

Returns true if leanback view outline is enabled on the system or false otherwise.

public booleanisUsingZOrder(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 voidonBindRowViewHolder(RowPresenter.ViewHolder vh, java.lang.Object item)

Binds the given row object to the given ViewHolder.

protected voidonRowViewExpanded(RowPresenter.ViewHolder vh, boolean expanded)

Called when the row view's expanded state changes.

protected voidonRowViewSelected(RowPresenter.ViewHolder vh, boolean selected)

Called when the given row view changes selection state.

protected voidonSelectLevelChanged(RowPresenter.ViewHolder holder)

Applies select level to header and draws a default color dim over each child of HorizontalGridView.

protected voidonUnbindRowViewHolder(RowPresenter.ViewHolder vh)

Unbinds the given ViewHolder.

public voidsetEntranceTransitionState(RowPresenter.ViewHolder holder, boolean afterEntrance)

Changes the visibility of views.

public voidsetExpandedRowHeight(int rowHeight)

Sets the expanded row height for rows created by this Presenter.

public final voidsetHoverCardPresenterSelector(PresenterSelector selector)

Sets the PresenterSelector used for showing a select object in a hover card.

public final voidsetKeepChildForeground(boolean keep)

When ListRowPresenter applies overlay color on the child, it may change child's foreground Drawable.

public voidsetNumRows(int numRows)

Sets the numbers of rows for rendering the list of items.

public voidsetRecycledPoolSize(Presenter presenter, int size)

Sets the recycled pool size for the given presenter.

public voidsetRowHeight(int rowHeight)

Sets the row height for rows created by this Presenter.

public final voidsetShadowEnabled(boolean enabled)

Enables or disables child shadow.

from RowPresentergetHeaderPresenter, getRowViewHolder, getSelectEffectEnabled, getSelectLevel, getSyncActivatePolicy, isClippingChildren, onBindViewHolder, onCreateViewHolder, onRowViewAttachedToWindow, onRowViewDetachedFromWindow, onUnbindViewHolder, onViewAttachedToWindow, onViewDetachedFromWindow, setHeaderPresenter, setRowViewExpanded, setRowViewSelected, setSelectEffectEnabled, setSelectLevel, setSyncActivatePolicy
from PresentercancelAnimationsRecursive, getFacet, onBindViewHolder, setFacet, setOnClickListener
from java.lang.Objectclone, 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.

protected void initializeRowViewHolder(RowPresenter.ViewHolder vh)

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.

public final void setHoverCardPresenterSelector(PresenterSelector selector)

Sets the PresenterSelector used for showing a select object in a hover card.

public final PresenterSelector getHoverCardPresenterSelector()

Returns the PresenterSelector used for showing a select object in a hover card.

protected abstract RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent)

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.

protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected)

Dispatch item selected event using current selected item in the HorizontalGridView. The method should only be called from onRowViewSelected().

protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected)

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.

protected void onRowViewExpanded(RowPresenter.ViewHolder vh, boolean expanded)

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.

protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, java.lang.Object item)

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.

protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh)

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.

protected ShadowOverlayHelper.Options createShadowOverlayOptions()

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.

protected void onSelectLevelChanged(RowPresenter.ViewHolder holder)

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)

protected void applySelectLevelToChild(ListRowPresenter.ViewHolder rowViewHolder, View childView)

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)

public void freeze(RowPresenter.ViewHolder holder, boolean freeze)

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.

public void setEntranceTransitionState(RowPresenter.ViewHolder holder, boolean afterEntrance)

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.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(View rootView, HorizontalGridView gridView, 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.
         */
        public final ListRowPresenter getListRowPresenter() {
            return mListRowPresenter;
        }

        /**
         * Gets HorizontalGridView that shows a list of items.
         * @return HorizontalGridView that shows a list of items.
         */
        public final HorizontalGridView getGridView() {
            return mGridView;
        }

        /**
         * Gets ItemBridgeAdapter that creates the list of items.
         * @return ItemBridgeAdapter that creates the list of items.
         */
        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.
         */
        public Presenter.ViewHolder getItemViewHolder(int position) {
            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
                    .findViewHolderForAdapterPosition(position);
            if (ibvh == null) {
                return null;
            }
            return ibvh.getViewHolder();
        }

        @Override
        public Presenter.ViewHolder getSelectedItemViewHolder() {
            return getItemViewHolder(getSelectedPosition());
        }

        @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.
         */
        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(Presenter.ViewHolderTask itemTask) {
            mItemTask = itemTask;
        }

        @Override
        public void run(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(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 holder) {
        super.initializeRowViewHolder(holder);
        final ViewHolder rowViewHolder = (ViewHolder) holder;
        Context context = holder.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(ViewGroup parent, 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(RowPresenter.ViewHolder holder, 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(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(RowPresenter.ViewHolder holder, boolean freeze) {
        ViewHolder vh = (ViewHolder) holder;
        vh.mGridView.setScrollEnabled(!freeze);
        vh.mGridView.setAnimateChildLayout(!freeze);
    }

    @Override
    public void setEntranceTransitionState(RowPresenter.ViewHolder holder,
            boolean afterEntrance) {
        super.setEntranceTransitionState(holder, afterEntrance);
        ((ViewHolder) holder).mGridView.setChildrenVisibility(
                afterEntrance? View.VISIBLE : View.INVISIBLE);
    }
}