public class

FullWidthDetailsOverviewRowPresenter

extends RowPresenter

 java.lang.Object

androidx.leanback.widget.Presenter

androidx.leanback.widget.RowPresenter

↳androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter

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.FullWidthDetailsOverviewRowPresenter android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter

Overview

Renders a DetailsOverviewRow to display an overview of an item. Typically this row will be the first row in a fragment such as the DetailsFragment. The View created by the FullWidthDetailsOverviewRowPresenter is made in three parts: logo view on the left, action list view on the top and a customizable detailed description view on the right.

The detailed description is rendered using a Presenter passed in FullWidthDetailsOverviewRowPresenter.FullWidthDetailsOverviewRowPresenter(Presenter). Typically this will be an instance of AbstractDetailsDescriptionPresenter. The application can access the detailed description ViewHolder from FullWidthDetailsOverviewRowPresenter.ViewHolder.getDetailsDescriptionViewHolder().

The logo view is rendered using a customizable DetailsOverviewLogoPresenter passed in FullWidthDetailsOverviewRowPresenter.FullWidthDetailsOverviewRowPresenter(Presenter, DetailsOverviewLogoPresenter). The application can access the logo ViewHolder from FullWidthDetailsOverviewRowPresenter.ViewHolder.getLogoViewHolder().

To support activity shared element transition, call FullWidthDetailsOverviewRowPresenter.setListener(FullWidthDetailsOverviewRowPresenter.Listener) with FullWidthDetailsOverviewSharedElementHelper during Activity's onCreate(). Application is free to create its own "shared element helper" class using the Listener for image binding. Call FullWidthDetailsOverviewRowPresenter.setParticipatingEntranceTransition(boolean) with false

The view has three states: FullWidthDetailsOverviewRowPresenter.STATE_HALF FullWidthDetailsOverviewRowPresenter.STATE_FULL and FullWidthDetailsOverviewRowPresenter.STATE_SMALL. See DetailsFragment where it switches states based on selected row position.

Summary

Fields
public static final intALIGN_MODE_MIDDLE

This is the alignment mode that the ending edge of logo and the starting edge of description align to the middle of the overview view.

public static final intALIGN_MODE_START

This is the alignment mode that the logo and description align to the starting edge of the overview view.

protected intmInitialState

public static final intSTATE_FULL

This is the state when the view covers full width and height of screen.

public static final intSTATE_HALF

This is the default state corresponding to layout file.

public static final intSTATE_SMALL

This is the state where the view shrinks to a small banner.

from RowPresenterSYNC_ACTIVATED_CUSTOM, SYNC_ACTIVATED_TO_EXPANDED, SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED, SYNC_ACTIVATED_TO_SELECTED
Constructors
publicFullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter)

Constructor for a FullWidthDetailsOverviewRowPresenter.

publicFullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter, DetailsOverviewLogoPresenter logoPresenter)

Constructor for a FullWidthDetailsOverviewRowPresenter.

Methods
protected abstract RowPresenter.ViewHoldercreateRowViewHolder(ViewGroup parent)

Called to create a ViewHolder object for a Row.

public final intgetActionsBackgroundColor()

Returns the background color of actions.

public final intgetAlignmentMode()

Returns alignment mode of Description.

public final intgetBackgroundColor()

Returns the background color.

public final intgetInitialState()

Returns the initial state used to create ViewHolder.

protected intgetLayoutResourceId()

Get resource id to inflate the layout.

public OnActionClickedListenergetOnActionClickedListener()

Returns the listener for Action click events.

protected booleanisClippingChildren()

Returns true if the Row view should clip its children.

public final booleanisParticipatingEntranceTransition()

Returns true if the overview should be part of shared element transition.

public booleanisUsingDefaultSelectEffect()

Returns true if this RowPresenter is using the default dimming effect.

public final voidnotifyOnBindLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder)

Called by DetailsOverviewLogoPresenter to notify logo was bound to view.

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

Binds the given row object to the given ViewHolder.

protected voidonLayoutLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState, boolean logoChanged)

Layout logo position based on current state.

protected voidonLayoutOverviewFrame(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState, boolean logoChanged)

Layout overview frame based on current state.

protected voidonRowViewAttachedToWindow(RowPresenter.ViewHolder vh)

Invoked when the row view is attached to the window.

protected voidonRowViewDetachedFromWindow(RowPresenter.ViewHolder vh)

Invoked when the row view is detached from the window.

protected voidonSelectLevelChanged(RowPresenter.ViewHolder vh)

Callback when the select level changes.

protected voidonStateChanged(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState)

Called when FullWidthDetailsOverviewRowPresenter.ViewHolder.getState() changes.

protected voidonUnbindRowViewHolder(RowPresenter.ViewHolder vh)

Unbinds the given ViewHolder.

public final voidsetActionsBackgroundColor(int color)

Sets the background color for Action Bar.

public final voidsetAlignmentMode(int alignmentMode)

Set alignment mode of Description.

public final voidsetBackgroundColor(int color)

Sets the background color.

public voidsetEntranceTransitionState(RowPresenter.ViewHolder holder, boolean afterEntrance)

Changes the visibility of views.

public final voidsetInitialState(int state)

Change the initial state used to create ViewHolder.

public final voidsetListener(FullWidthDetailsOverviewRowPresenter.Listener listener)

Set listener for details overview presenter.

public voidsetOnActionClickedListener(OnActionClickedListener listener)

Sets the listener for Action click events.

public final voidsetParticipatingEntranceTransition(boolean participating)

Sets if the overview should be part of shared element transition.

public final voidsetState(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int state)

Switch state of a ViewHolder.

from RowPresenterdispatchItemSelectedListener, freeze, getHeaderPresenter, getRowViewHolder, getSelectEffectEnabled, getSelectLevel, getSyncActivatePolicy, initializeRowViewHolder, onBindViewHolder, onCreateViewHolder, onRowViewExpanded, onRowViewSelected, 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

Fields

public static final int STATE_HALF

This is the default state corresponding to layout file. The view takes full width of screen and covers bottom half of the screen.

public static final int STATE_FULL

This is the state when the view covers full width and height of screen.

public static final int STATE_SMALL

This is the state where the view shrinks to a small banner.

public static final int ALIGN_MODE_START

This is the alignment mode that the logo and description align to the starting edge of the overview view.

public static final int ALIGN_MODE_MIDDLE

This is the alignment mode that the ending edge of logo and the starting edge of description align to the middle of the overview view. Note that this might not be the exact horizontal center of the overview view.

protected int mInitialState

Constructors

public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter)

Constructor for a FullWidthDetailsOverviewRowPresenter.

Parameters:

detailsPresenter: The Presenter used to render the detailed description of the row.

public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter, DetailsOverviewLogoPresenter logoPresenter)

Constructor for a FullWidthDetailsOverviewRowPresenter.

Parameters:

detailsPresenter: The Presenter used to render the detailed description of the row.
logoPresenter: The Presenter used to render the logo view.

Methods

public void setOnActionClickedListener(OnActionClickedListener listener)

Sets the listener for Action click events.

public OnActionClickedListener getOnActionClickedListener()

Returns the listener for Action click events.

public final void setBackgroundColor(int color)

Sets the background color. If not set, a default from the theme will be used.

public final int getBackgroundColor()

Returns the background color. If FullWidthDetailsOverviewRowPresenter.setBackgroundColor(int), transparent is returned.

public final void setActionsBackgroundColor(int color)

Sets the background color for Action Bar. If not set, a default from the theme will be used.

public final int getActionsBackgroundColor()

Returns the background color of actions. If FullWidthDetailsOverviewRowPresenter.setActionsBackgroundColor(int) is not called, transparent is returned.

public final boolean isParticipatingEntranceTransition()

Returns true if the overview should be part of shared element transition.

public final void setParticipatingEntranceTransition(boolean participating)

Sets if the overview should be part of shared element transition.

public final void setInitialState(int state)

Change the initial state used to create ViewHolder.

public final int getInitialState()

Returns the initial state used to create ViewHolder.

public final void setAlignmentMode(int alignmentMode)

Set alignment mode of Description.

Parameters:

alignmentMode: One of FullWidthDetailsOverviewRowPresenter.ALIGN_MODE_MIDDLE or FullWidthDetailsOverviewRowPresenter.ALIGN_MODE_START

public final int getAlignmentMode()

Returns alignment mode of Description.

Returns:

One of FullWidthDetailsOverviewRowPresenter.ALIGN_MODE_MIDDLE or FullWidthDetailsOverviewRowPresenter.ALIGN_MODE_START.

protected boolean isClippingChildren()

Returns true if the Row view should clip its children. The clipChildren flag is set on view in RowPresenter.initializeRowViewHolder(RowPresenter.ViewHolder). Note that Slide transition or explode transition need turn off clipChildren. Default value is false.

public final void setListener(FullWidthDetailsOverviewRowPresenter.Listener listener)

Set listener for details overview presenter. Must be called before creating ViewHolder.

protected int getLayoutResourceId()

Get resource id to inflate the layout. The layout must match FullWidthDetailsOverviewRowPresenter.STATE_HALF

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 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 boolean isUsingDefaultSelectEffect()

Returns true if this RowPresenter is using the default dimming effect. A subclass may (most likely) return false and override RowPresenter.onSelectLevelChanged(RowPresenter.ViewHolder).

protected void onSelectLevelChanged(RowPresenter.ViewHolder vh)

Callback when the select level changes. The default implementation applies the select level to RowHeaderPresenter.setSelectLevel(RowHeaderPresenter.ViewHolder, float) when RowPresenter.getSelectEffectEnabled() is true. Subclasses may override this function and implement a different select effect. In this case, the method RowPresenter.isUsingDefaultSelectEffect() should also be overridden to disable the default dimming effect.

protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh)

Invoked when the row view is attached to the window.

protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh)

Invoked when the row view is detached from the window.

public final void notifyOnBindLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder)

Called by DetailsOverviewLogoPresenter to notify logo was bound to view. Application should not directly call this method.

Parameters:

viewHolder: The row ViewHolder that has logo bound to view.

protected void onLayoutLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState, boolean logoChanged)

Layout logo position based on current state. Subclass may override. The method is called when a logo is bound to view or state changes.

Parameters:

viewHolder: The row ViewHolder that contains the logo.
oldState: The old state, can be same as current viewHolder.getState()
logoChanged: Whether logo was changed.

protected void onLayoutOverviewFrame(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState, boolean logoChanged)

Layout overview frame based on current state. Subclass may override. The method is called when a logo is bound to view or state changes.

Parameters:

viewHolder: The row ViewHolder that contains the logo.
oldState: The old state, can be same as current viewHolder.getState()
logoChanged: Whether logo was changed.

public final void setState(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int state)

Switch state of a ViewHolder.

Parameters:

viewHolder: The ViewHolder to change state.
state: New state, can be FullWidthDetailsOverviewRowPresenter.STATE_FULL, FullWidthDetailsOverviewRowPresenter.STATE_HALF or FullWidthDetailsOverviewRowPresenter.STATE_SMALL.

protected void onStateChanged(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int oldState)

Called when FullWidthDetailsOverviewRowPresenter.ViewHolder.getState() changes. Subclass may override. The default implementation calls FullWidthDetailsOverviewRowPresenter.onLayoutLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean) and FullWidthDetailsOverviewRowPresenter.onLayoutOverviewFrame(FullWidthDetailsOverviewRowPresenter.ViewHolder, int, boolean).

Parameters:

viewHolder: The ViewHolder which state changed.
oldState: The old state.

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.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.leanback.R;
import androidx.recyclerview.widget.RecyclerView;

/**
 * Renders a {@link DetailsOverviewRow} to display an overview of an item. Typically this row will
 * be the first row in a fragment such as the
 * {@link androidx.leanback.app.DetailsFragment}. The View created by the
 * FullWidthDetailsOverviewRowPresenter is made in three parts: logo view on the left, action list view on
 * the top and a customizable detailed description view on the right.
 *
 * <p>The detailed description is rendered using a {@link Presenter} passed in
 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter)}. Typically this will be an instance of
 * {@link AbstractDetailsDescriptionPresenter}. The application can access the detailed description
 * ViewHolder from {@link ViewHolder#getDetailsDescriptionViewHolder()}.
 * </p>
 *
 * <p>The logo view is rendered using a customizable {@link DetailsOverviewLogoPresenter} passed in
 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter, DetailsOverviewLogoPresenter)}. The application
 * can access the logo ViewHolder from {@link ViewHolder#getLogoViewHolder()}.
 * </p>
 *
 * <p>
 * To support activity shared element transition, call {@link #setListener(Listener)} with
 * {@link FullWidthDetailsOverviewSharedElementHelper} during Activity's onCreate(). Application is free to
 * create its own "shared element helper" class using the Listener for image binding.
 * Call {@link #setParticipatingEntranceTransition(boolean)} with false
 * </p>
 *
 * <p>
 * The view has three states: {@link #STATE_HALF} {@link #STATE_FULL} and {@link #STATE_SMALL}. See
 * {@link androidx.leanback.app.DetailsFragment} where it switches states based on
 * selected row position.
 * </p>
 */
public class FullWidthDetailsOverviewRowPresenter extends RowPresenter {

    static final String TAG = "FullWidthDetailsRP";
    static final boolean DEBUG = false;

    static final Handler sHandler = new Handler();

    /**
     * This is the default state corresponding to layout file.  The view takes full width
     * of screen and covers bottom half of the screen.
     */
    public static final int STATE_HALF = 0;
    /**
     * This is the state when the view covers full width and height of screen.
     */
    public static final int STATE_FULL = 1;
    /**
     * This is the state where the view shrinks to a small banner.
     */
    public static final int STATE_SMALL = 2;

    /**
     * This is the alignment mode that the logo and description align to the starting edge of the
     * overview view.
     */
    public static final int ALIGN_MODE_START = 0;
    /**
     * This is the alignment mode that the ending edge of logo and the starting edge of description
     * align to the middle of the overview view. Note that this might not be the exact horizontal
     * center of the overview view.
     */
    public static final int ALIGN_MODE_MIDDLE = 1;

    /**
     * Listeners for events on ViewHolder.
     */
    public static abstract class Listener {

        /**
         * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(ViewHolder)} is called.
         * @param vh  The ViewHolder that has bound logo view.
         */
        public void onBindLogo(ViewHolder vh) {
        }

    }

    class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
        FullWidthDetailsOverviewRowPresenter.ViewHolder mViewHolder;

        ActionsItemBridgeAdapter(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder) {
            mViewHolder = viewHolder;
        }

        @Override
        @SuppressWarnings("unchecked")
        public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
            if (mViewHolder.getOnItemViewClickedListener() != null
                    || mActionClickedListener != null) {
                ibvh.getPresenter().setOnClickListener(
                        ibvh.getViewHolder(), new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if (mViewHolder.getOnItemViewClickedListener() != null) {
                                    mViewHolder.getOnItemViewClickedListener().onItemClicked(
                                            ibvh.getViewHolder(), ibvh.getItem(),
                                            mViewHolder, mViewHolder.getRow());
                                }
                                if (mActionClickedListener != null) {
                                    mActionClickedListener.onActionClicked((Action) ibvh.getItem());
                                }
                            }
                        });
            }
        }
        @Override
        public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
            if (mViewHolder.getOnItemViewClickedListener() != null
                    || mActionClickedListener != null) {
                ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
            }
        }
        @Override
        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
            // Remove first to ensure we don't add ourselves more than once.
            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
            viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
        }
        @Override
        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
            mViewHolder.checkFirstAndLastPosition(false);
        }
    }

    /**
     * A ViewHolder for the DetailsOverviewRow.
     */
    public class ViewHolder extends RowPresenter.ViewHolder {

        protected final DetailsOverviewRow.Listener mRowListener = createRowListener();

        protected DetailsOverviewRow.Listener createRowListener() {
            return new DetailsOverviewRowListener();
        }

        public class DetailsOverviewRowListener extends DetailsOverviewRow.Listener {
            @Override
            public void onImageDrawableChanged(@NonNull DetailsOverviewRow row) {
                sHandler.removeCallbacks(mUpdateDrawableCallback);
                sHandler.post(mUpdateDrawableCallback);
            }

            @Override
            public void onItemChanged(@NonNull DetailsOverviewRow row) {
                if (mDetailsDescriptionViewHolder != null) {
                    mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
                }
                mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
            }

            @Override
            public void onActionsAdapterChanged(@NonNull DetailsOverviewRow row) {
                bindActions(row.getActionsAdapter());
            }
        };

        final ViewGroup mOverviewRoot;
        final FrameLayout mOverviewFrame;
        final ViewGroup mDetailsDescriptionFrame;
        final HorizontalGridView mActionsRow;
        final Presenter.ViewHolder mDetailsDescriptionViewHolder;
        final DetailsOverviewLogoPresenter.ViewHolder mDetailsLogoViewHolder;
        int mNumItems;
        ItemBridgeAdapter mActionBridgeAdapter;
        int mState = STATE_HALF;

        final Runnable mUpdateDrawableCallback = new Runnable() {
            @Override
            public void run() {
                Row row = getRow();
                if (row == null) {
                    return;
                }
                mDetailsOverviewLogoPresenter.onBindViewHolder(mDetailsLogoViewHolder, row);
            }
        };

        void bindActions(ObjectAdapter adapter) {
            mActionBridgeAdapter.setAdapter(adapter);
            mActionsRow.setAdapter(mActionBridgeAdapter);
            mNumItems = mActionBridgeAdapter.getItemCount();

        }

        void unbindActions() {
            mActionBridgeAdapter.setAdapter(null);
            mActionsRow.setAdapter(null);
            mNumItems = 0;

        }

        void onBind() {
            DetailsOverviewRow row = (DetailsOverviewRow) getRow();
            bindActions(row.getActionsAdapter());
            row.addListener(mRowListener);
        }

        void onUnbind() {
            unbindActions();
            DetailsOverviewRow row = (DetailsOverviewRow) getRow();
            row.removeListener(mRowListener);
            sHandler.removeCallbacks(mUpdateDrawableCallback);
        }

        final View.OnLayoutChangeListener mLayoutChangeListener =
                new View.OnLayoutChangeListener() {

            @Override
            public void onLayoutChange(View v, int left, int top, int right,
                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
                checkFirstAndLastPosition(false);
            }
        };

        final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() {
            @Override
            public void onChildSelected(
                    @NonNull ViewGroup parent,
                    @Nullable View view,
                    int position,
                    long id
            ) {
                dispatchItemSelection(view);
            }
        };

        @SuppressWarnings("unchecked")
        void dispatchItemSelection(View view) {
            if (!isSelected()) {
                return;
            }
            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null
                    ? mActionsRow.getChildViewHolder(view)
                    : mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
            if (ibvh == null) {
                if (getOnItemViewSelectedListener() != null) {
                    getOnItemViewSelectedListener().onItemSelected(null, null,
                            ViewHolder.this, getRow());
                }
            } else {
                if (getOnItemViewSelectedListener() != null) {
                    getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
                            ViewHolder.this, getRow());
                }
            }
        };

        final RecyclerView.OnScrollListener mScrollListener =
                new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            }
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                checkFirstAndLastPosition(true);
            }
        };

        void checkFirstAndLastPosition(boolean fromScroll) {
            RecyclerView.ViewHolder viewHolder;

            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
            boolean showRight = (viewHolder == null
                    || viewHolder.itemView.getRight() > mActionsRow.getWidth());

            viewHolder = mActionsRow.findViewHolderForPosition(0);
            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);

            if (DEBUG) {
                Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll
                        + " showRight " + showRight + " showLeft " + showLeft);
            }

        }

        /**
         * Constructor for the ViewHolder.
         *
         * @param rootView The root View that this view holder will be attached
         *        to.
         */
        public ViewHolder(View rootView, Presenter detailsPresenter,
                DetailsOverviewLogoPresenter logoPresenter) {
            super(rootView);
            mOverviewRoot = (ViewGroup) rootView.findViewById(R.id.details_root);
            mOverviewFrame = (FrameLayout) rootView.findViewById(R.id.details_frame);
            mDetailsDescriptionFrame =
                    (ViewGroup) rootView.findViewById(R.id.details_overview_description);
            mActionsRow =
                    (HorizontalGridView) mOverviewFrame.findViewById(R.id.details_overview_actions);
            mActionsRow.setHasOverlappingRendering(false);
            mActionsRow.setOnScrollListener(mScrollListener);
            mActionsRow.setAdapter(mActionBridgeAdapter);
            mActionsRow.setOnChildSelectedListener(mChildSelectedListener);

            final int fadeLength = rootView.getResources().getDimensionPixelSize(
                    R.dimen.lb_details_overview_actions_fade_size);
            mActionsRow.setFadingRightEdgeLength(fadeLength);
            mActionsRow.setFadingLeftEdgeLength(fadeLength);
            mDetailsDescriptionViewHolder =
                    detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
            mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
            mDetailsLogoViewHolder = (DetailsOverviewLogoPresenter.ViewHolder)
                    logoPresenter.onCreateViewHolder(mOverviewRoot);
            mOverviewRoot.addView(mDetailsLogoViewHolder.view);
        }

        /**
         * Returns the rectangle area with a color background.
         */
        public final ViewGroup getOverviewView() {
            return mOverviewFrame;
        }

        /**
         * Returns the ViewHolder for logo.
         */
        public final DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder() {
            return mDetailsLogoViewHolder;
        }

        /**
         * Returns the ViewHolder for DetailsDescription.
         */
        public final Presenter.ViewHolder getDetailsDescriptionViewHolder() {
            return mDetailsDescriptionViewHolder;
        }

        /**
         * Returns the root view for inserting details description.
         */
        public final ViewGroup getDetailsDescriptionFrame() {
            return mDetailsDescriptionFrame;
        }

        /**
         * Returns the view of actions row.
         */
        public final ViewGroup getActionsRow() {
            return mActionsRow;
        }

        /**
         * Returns current state of the ViewHolder set by
         * {@link FullWidthDetailsOverviewRowPresenter#setState(ViewHolder, int)}.
         */
        public final int getState() {
            return mState;
        }
    }

    protected int mInitialState = STATE_HALF;

    final Presenter mDetailsPresenter;
    final DetailsOverviewLogoPresenter mDetailsOverviewLogoPresenter;
    OnActionClickedListener mActionClickedListener;

    private int mBackgroundColor = Color.TRANSPARENT;
    private int mActionsBackgroundColor = Color.TRANSPARENT;
    private boolean mBackgroundColorSet;
    private boolean mActionsBackgroundColorSet;

    private Listener mListener;
    private boolean mParticipatingEntranceTransition;

    private int mAlignmentMode;

    /**
     * Constructor for a FullWidthDetailsOverviewRowPresenter.
     *
     * @param detailsPresenter The {@link Presenter} used to render the detailed
     *        description of the row.
     */
    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter) {
        this(detailsPresenter, new DetailsOverviewLogoPresenter());
    }

    /**
     * Constructor for a FullWidthDetailsOverviewRowPresenter.
     *
     * @param detailsPresenter The {@link Presenter} used to render the detailed
     *        description of the row.
     * @param logoPresenter  The {@link Presenter} used to render the logo view.
     */
    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter,
            DetailsOverviewLogoPresenter logoPresenter) {
        setHeaderPresenter(null);
        setSelectEffectEnabled(false);
        mDetailsPresenter = detailsPresenter;
        mDetailsOverviewLogoPresenter = logoPresenter;
    }

    /**
     * Sets the listener for Action click events.
     */
    public void setOnActionClickedListener(OnActionClickedListener listener) {
        mActionClickedListener = listener;
    }

    /**
     * Returns the listener for Action click events.
     */
    public OnActionClickedListener getOnActionClickedListener() {
        return mActionClickedListener;
    }

    /**
     * Sets the background color.  If not set, a default from the theme will be used.
     */
    public final void setBackgroundColor(int color) {
        mBackgroundColor = color;
        mBackgroundColorSet = true;
    }

    /**
     * Returns the background color.  If {@link #setBackgroundColor(int)}, transparent
     * is returned.
     */
    public final int getBackgroundColor() {
        return mBackgroundColor;
    }

    /**
     * Sets the background color for Action Bar.  If not set, a default from the theme will be
     * used.
     */
    public final void setActionsBackgroundColor(int color) {
        mActionsBackgroundColor = color;
        mActionsBackgroundColorSet = true;
    }

    /**
     * Returns the background color of actions.  If {@link #setActionsBackgroundColor(int)}
     * is not called,  transparent is returned.
     */
    public final int getActionsBackgroundColor() {
        return mActionsBackgroundColor;
    }

    /**
     * Returns true if the overview should be part of shared element transition.
     */
    public final boolean isParticipatingEntranceTransition() {
        return mParticipatingEntranceTransition;
    }

    /**
     * Sets if the overview should be part of shared element transition.
     */
    public final void setParticipatingEntranceTransition(boolean participating) {
        mParticipatingEntranceTransition = participating;
    }

    /**
     * Change the initial state used to create ViewHolder.
     */
    public final void setInitialState(int state) {
        mInitialState = state;
    }

    /**
     * Returns the initial state used to create ViewHolder.
     */
    public final int getInitialState() {
        return mInitialState;
    }

    /**
     * Set alignment mode of Description.
     *
     * @param alignmentMode  One of {@link #ALIGN_MODE_MIDDLE} or {@link #ALIGN_MODE_START}
     */
    public final void setAlignmentMode(int alignmentMode) {
        mAlignmentMode = alignmentMode;
    }

    /**
     * Returns alignment mode of Description.
     *
     * @return  One of {@link #ALIGN_MODE_MIDDLE} or {@link #ALIGN_MODE_START}.
     */
    public final int getAlignmentMode() {
        return mAlignmentMode;
    }

    @Override
    protected boolean isClippingChildren() {
        return true;
    }

    /**
     * Set listener for details overview presenter. Must be called before creating
     * ViewHolder.
     */
    public final void setListener(Listener listener) {
        mListener = listener;
    }

    /**
     * Get resource id to inflate the layout.  The layout must match {@link #STATE_HALF}
     */
    protected int getLayoutResourceId() {
        return R.layout.lb_fullwidth_details_overview;
    }

    @Override
    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
        View v = LayoutInflater.from(parent.getContext())
            .inflate(getLayoutResourceId(), parent, false);
        final ViewHolder vh = new ViewHolder(v, mDetailsPresenter, mDetailsOverviewLogoPresenter);
        mDetailsOverviewLogoPresenter.setContext(vh.mDetailsLogoViewHolder, vh, this);
        setState(vh, mInitialState);

        vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
        final View overview = vh.mOverviewFrame;
        if (mBackgroundColorSet) {
            overview.setBackgroundColor(mBackgroundColor);
        }
        if (mActionsBackgroundColorSet) {
            overview.findViewById(R.id.details_overview_actions_background)
                    .setBackgroundColor(mActionsBackgroundColor);
        }
        RoundedRectHelper.setClipToRoundedOutline(overview, true);

        if (!getSelectEffectEnabled()) {
            vh.mOverviewFrame.setForeground(null);
        }

        vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
            @Override
            public boolean onUnhandledKey(KeyEvent event) {
                if (vh.getOnKeyListener() != null) {
                    return vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event);
                }
                return false;
            }
        });
        return vh;
    }

    @Override
    protected void onBindRowViewHolder(
            @NonNull RowPresenter.ViewHolder holder,
            @NonNull Object item
    ) {
        super.onBindRowViewHolder(holder, item);

        DetailsOverviewRow row = (DetailsOverviewRow) item;
        ViewHolder vh = (ViewHolder) holder;

        mDetailsOverviewLogoPresenter.onBindViewHolder(vh.mDetailsLogoViewHolder, row);
        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
        vh.onBind();
    }

    @Override
    protected void onUnbindRowViewHolder(@NonNull RowPresenter.ViewHolder holder) {
        ViewHolder vh = (ViewHolder) holder;
        vh.onUnbind();
        mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
        mDetailsOverviewLogoPresenter.onUnbindViewHolder(vh.mDetailsLogoViewHolder);
        super.onUnbindRowViewHolder(holder);
    }

    @Override
    public final boolean isUsingDefaultSelectEffect() {
        return false;
    }

    @Override
    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
        super.onSelectLevelChanged(holder);
        if (getSelectEffectEnabled()) {
            ViewHolder vh = (ViewHolder) holder;
            int dimmedColor = vh.mColorDimmer.getPaint().getColor();
            ((ColorDrawable) vh.mOverviewFrame.getForeground().mutate()).setColor(dimmedColor);
        }
    }

    @Override
    protected void onRowViewAttachedToWindow(@NonNull RowPresenter.ViewHolder vh) {
        super.onRowViewAttachedToWindow(vh);
        ViewHolder viewHolder = (ViewHolder) vh;
        mDetailsPresenter.onViewAttachedToWindow(viewHolder.mDetailsDescriptionViewHolder);
        mDetailsOverviewLogoPresenter.onViewAttachedToWindow(viewHolder.mDetailsLogoViewHolder);
    }

    @Override
    protected void onRowViewDetachedFromWindow(@NonNull RowPresenter.ViewHolder vh) {
        super.onRowViewDetachedFromWindow(vh);
        ViewHolder viewHolder = (ViewHolder) vh;
        mDetailsPresenter.onViewDetachedFromWindow(viewHolder.mDetailsDescriptionViewHolder);
        mDetailsOverviewLogoPresenter.onViewDetachedFromWindow(viewHolder.mDetailsLogoViewHolder);
    }

    /**
     * Called by {@link DetailsOverviewLogoPresenter} to notify logo was bound to view.
     * Application should not directly call this method.
     * @param viewHolder  The row ViewHolder that has logo bound to view.
     */
    public final void notifyOnBindLogo(ViewHolder viewHolder) {
        onLayoutOverviewFrame(viewHolder, viewHolder.getState(), true);
        onLayoutLogo(viewHolder, viewHolder.getState(), true);
        if (mListener != null) {
            mListener.onBindLogo(viewHolder);
        }
    }

    /**
     * Layout logo position based on current state.  Subclass may override.
     * The method is called when a logo is bound to view or state changes.
     * @param viewHolder  The row ViewHolder that contains the logo.
     * @param oldState    The old state,  can be same as current viewHolder.getState()
     * @param logoChanged Whether logo was changed.
     */
    protected void onLayoutLogo(ViewHolder viewHolder, int oldState, boolean logoChanged) {
        View v = viewHolder.getLogoViewHolder().view;
        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
                v.getLayoutParams();
        switch (mAlignmentMode) {
            case ALIGN_MODE_START:
            default:
                lp.setMarginStart(v.getResources().getDimensionPixelSize(
                        R.dimen.lb_details_v2_logo_margin_start));
                break;
            case ALIGN_MODE_MIDDLE:
                lp.setMarginStart(v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_left)
                        - lp.width);
                break;
        }

        switch (viewHolder.getState()) {
        case STATE_FULL:
        default:
            lp.topMargin =
                    v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_blank_height)
                    - lp.height / 2;
            break;
        case STATE_HALF:
            lp.topMargin = v.getResources().getDimensionPixelSize(
                    R.dimen.lb_details_v2_blank_height) + v.getResources()
                    .getDimensionPixelSize(R.dimen.lb_details_v2_actions_height) + v
                    .getResources().getDimensionPixelSize(
                    R.dimen.lb_details_v2_description_margin_top);
            break;
        case STATE_SMALL:
            lp.topMargin = 0;
            break;
        }
        v.setLayoutParams(lp);
    }

    /**
     * Layout overview frame based on current state.  Subclass may override.
     * The method is called when a logo is bound to view or state changes.
     * @param viewHolder  The row ViewHolder that contains the logo.
     * @param oldState    The old state,  can be same as current viewHolder.getState()
     * @param logoChanged Whether logo was changed.
     */
    protected void onLayoutOverviewFrame(ViewHolder viewHolder, int oldState, boolean logoChanged) {
        boolean wasBanner = oldState == STATE_SMALL;
        boolean isBanner = viewHolder.getState() == STATE_SMALL;
        if (wasBanner != isBanner || logoChanged) {
            Resources res = viewHolder.view.getResources();

            int frameMarginStart;
            int descriptionMarginStart = 0;
            int logoWidth = 0;
            if (mDetailsOverviewLogoPresenter.isBoundToImage(viewHolder.getLogoViewHolder(),
                    (DetailsOverviewRow) viewHolder.getRow())) {
                logoWidth = viewHolder.getLogoViewHolder().view.getLayoutParams().width;
            }
            switch (mAlignmentMode) {
                case ALIGN_MODE_START:
                default:
                    if (isBanner) {
                        frameMarginStart = res.getDimensionPixelSize(
                                R.dimen.lb_details_v2_logo_margin_start);
                        descriptionMarginStart = logoWidth;
                    } else {
                        frameMarginStart = 0;
                        descriptionMarginStart = logoWidth + res.getDimensionPixelSize(
                                R.dimen.lb_details_v2_logo_margin_start);
                    }
                    break;
                case ALIGN_MODE_MIDDLE:
                    if (isBanner) {
                        frameMarginStart = res.getDimensionPixelSize(R.dimen.lb_details_v2_left)
                                - logoWidth;
                        descriptionMarginStart = logoWidth;
                    } else {
                        frameMarginStart = 0;
                        descriptionMarginStart = res.getDimensionPixelSize(
                                R.dimen.lb_details_v2_left);
                    }
                    break;
            }
            MarginLayoutParams lpFrame =
                    (MarginLayoutParams) viewHolder.getOverviewView().getLayoutParams();
            lpFrame.topMargin = isBanner ? 0
                    : res.getDimensionPixelSize(R.dimen.lb_details_v2_blank_height);
            lpFrame.leftMargin = lpFrame.rightMargin = frameMarginStart;
            viewHolder.getOverviewView().setLayoutParams(lpFrame);

            View description = viewHolder.getDetailsDescriptionFrame();
            MarginLayoutParams lpDesc = (MarginLayoutParams) description.getLayoutParams();
            lpDesc.setMarginStart(descriptionMarginStart);
            description.setLayoutParams(lpDesc);

            View action = viewHolder.getActionsRow();
            MarginLayoutParams lpActions = (MarginLayoutParams) action.getLayoutParams();
            lpActions.setMarginStart(descriptionMarginStart);
            lpActions.height =
                    isBanner ? 0 : res.getDimensionPixelSize(R.dimen.lb_details_v2_actions_height);
            action.setLayoutParams(lpActions);
        }
    }

    /**
     * Switch state of a ViewHolder.
     * @param viewHolder   The ViewHolder to change state.
     * @param state        New state, can be {@link #STATE_FULL}, {@link #STATE_HALF}
     *                     or {@link #STATE_SMALL}.
     */
    public final void setState(ViewHolder viewHolder, int state) {
        if (viewHolder.getState() != state) {
            int oldState = viewHolder.getState();
            viewHolder.mState = state;
            onStateChanged(viewHolder, oldState);
        }
    }

    /**
     * Called when {@link ViewHolder#getState()} changes.  Subclass may override.
     * The default implementation calls {@link #onLayoutLogo(ViewHolder, int, boolean)} and
     * {@link #onLayoutOverviewFrame(ViewHolder, int, boolean)}.
     * @param viewHolder   The ViewHolder which state changed.
     * @param oldState     The old state.
     */
    protected void onStateChanged(ViewHolder viewHolder, int oldState) {
        onLayoutOverviewFrame(viewHolder, oldState, false);
        onLayoutLogo(viewHolder, oldState, false);
    }

    @Override
    public void setEntranceTransitionState(@NonNull RowPresenter.ViewHolder holder,
            boolean afterEntrance) {
        super.setEntranceTransitionState(holder, afterEntrance);
        if (mParticipatingEntranceTransition) {
            holder.view.setVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
        }
    }
}