/* * 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 android.support.v17.leanback.widget; import java.util.ArrayList; import android.graphics.Canvas; import android.support.v17.leanback.graphics.ColorOverlayDimmer; import android.support.v17.leanback.widget.Presenter.ViewHolder; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; /** * ListRowPresenter renders {@link ListRow} using a * {@link HorizontalGridView} hosted in a {@link BrowseRowView}. * *

Hover card

* 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. * *

Selection animation

* ListRowPresenter disables {@link RowPresenter}'s default dimming effect and draw * a dim overlay on top of each individual child items. Subclass may override and disable * {@link #isUsingDefaultListSelectEffect()} and write its own dim effect in * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)}. */ public class ListRowPresenter extends RowPresenter { private static final String TAG = "ListRowPresenter"; private static final boolean DEBUG = false; public static class ViewHolder extends RowPresenter.ViewHolder { final ListRowPresenter mListRowPresenter; final HorizontalGridView mGridView; final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter(); final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher(); final ColorOverlayDimmer mColorDimmer; public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) { super(rootView); mGridView = gridView; mListRowPresenter = p; mColorDimmer = ColorOverlayDimmer.createDefault(rootView.getContext()); } public final ListRowPresenter getListRowPresenter() { return mListRowPresenter; } public final HorizontalGridView getGridView() { return mGridView; } } private PresenterSelector mHoverCardPresenterSelector; @Override protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) { super.initializeRowViewHolder(holder); final ViewHolder rowViewHolder = (ViewHolder) holder; if (needsDefaultListItemDecoration()) { rowViewHolder.mGridView.addItemDecoration(new ItemDecoration(rowViewHolder)); } FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter); rowViewHolder.mGridView.setOnChildSelectedListener( new OnChildSelectedListener() { @Override public void onChildSelected(ViewGroup parent, View view, int position, long id) { selectChildView(rowViewHolder, view); } }); if (getOnItemClickedListener() != null) { // Only when having an OnItemClickListner, we will attach the OnClickListener. rowViewHolder.mItemBridgeAdapter.setAdapterListener( new ItemBridgeAdapter.AdapterListener() { @Override public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) rowViewHolder.mGridView.getChildViewHolder(v); if (getOnItemClickedListener() != null) { getOnItemClickedListener().onItemClicked(ibh.mItem, (ListRow) rowViewHolder.mRow); } } }); } }); } } private boolean needsDefaultListItemDecoration() { return isUsingDefaultListSelectEffect() && getSelectEffectEnabled(); } /** * Set {@link PresenterSelector} used for showing a select object in a hover card. */ public final void setHoverCardPresenterSelector(PresenterSelector selector) { mHoverCardPresenterSelector = selector; } /** * Get {@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. */ private void selectChildView(ViewHolder rowViewHolder, View view) { ItemBridgeAdapter.ViewHolder ibh = null; if (view != null) { ibh = (ItemBridgeAdapter.ViewHolder) rowViewHolder.mGridView.getChildViewHolder(view); } if (view == null) { if (mHoverCardPresenterSelector != null) { rowViewHolder.mHoverCardViewSwitcher.unselect(); } if (getOnItemSelectedListener() != null) { getOnItemSelectedListener().onItemSelected(null, rowViewHolder.mRow); } } else if (rowViewHolder.mExpanded && rowViewHolder.mSelected) { if (mHoverCardPresenterSelector != null) { rowViewHolder.mHoverCardViewSwitcher.select(rowViewHolder.mGridView, view, ibh.mItem); } if (getOnItemSelectedListener() != null) { getOnItemSelectedListener().onItemSelected(ibh.mItem, rowViewHolder.mRow); } } } @Override protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { BrowseRowView rowView = new BrowseRowView(parent.getContext()); return new ViewHolder(rowView, rowView.getGridView(), this); } @Override protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) { updateFooterViewSwitcher((ViewHolder) holder); updateInitialChildSelection((ViewHolder) holder); } /* * 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); } } else { if (mHoverCardPresenterSelector != null) { vh.mHoverCardViewSwitcher.clear(); } } } /* * Make initial child selection when row selection state is changed. */ private void updateInitialChildSelection(ViewHolder vh) { if (vh.mExpanded && vh.mSelected) { ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) vh.mGridView.findViewHolderForPosition( vh.mGridView.getSelectedPosition()); selectChildView(vh, ibh == null ? null : ibh.mHolder.view); } else { selectChildView(vh, null); } } @Override protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) { super.onRowViewExpanded(holder, expanded); ViewHolder vh = (ViewHolder) holder; vh.mGridView.setClipToPadding(!expanded); updateFooterViewSwitcher(vh); } @Override public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { super.onBindViewHolder(holder, item); ViewHolder vh = (ViewHolder)holder; ListRow rowItem = (ListRow) item; vh.mItemBridgeAdapter.clear(); vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter()); vh.mGridView.setAdapter(vh.mItemBridgeAdapter); } @Override public void onUnbindViewHolder(Presenter.ViewHolder holder) { ViewHolder vh = (ViewHolder)holder; vh.mGridView.setAdapter(null); super.onUnbindViewHolder(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. * @see #onSelectLevelChanged(RowPresenter.ViewHolder) */ public boolean isUsingDefaultListSelectEffect() { return true; } /** * Applies select level to header and draw a default color dim over each child * of {@link HorizontalGridView}. *

* Subclass may override this method. A subclass * needs to call super.onSelectLevelChanged() for applying header select level * and optionally applying a default select level to each child view of * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()} * is true. Subclass may override {@link #isUsingDefaultListSelectEffect()} to return * false and deal with the individual item select level by itself. *

*/ @Override protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) { super.onSelectLevelChanged(holder); if (needsDefaultListItemDecoration()) { ViewHolder vh = (ViewHolder) holder; vh.mColorDimmer.setActiveLevel(holder.mSelectLevel); vh.mGridView.invalidate(); } } private void drawDimSelectionForChildren(ViewHolder vh, Canvas c) { final ColorOverlayDimmer dimmer = vh.mColorDimmer; if (dimmer.needsDraw()) { final HorizontalGridView gridView = vh.mGridView; // Clip to padding when not expanded if (!vh.mExpanded) { c.clipRect(gridView.getPaddingLeft(), gridView.getPaddingTop(), gridView.getWidth() - gridView.getPaddingRight(), gridView.getHeight() - gridView.getPaddingBottom()); } for (int i = 0, count = gridView.getChildCount(); i < count; i++) { dimmer.drawColorOverlay(c, gridView.getChildAt(i), true); } } } final class ItemDecoration extends RecyclerView.ItemDecoration { ViewHolder mViewHolder; ItemDecoration(ViewHolder viewHolder) { mViewHolder = viewHolder; } @Override public void onDrawOver(Canvas c, RecyclerView parent) { drawDimSelectionForChildren(mViewHolder, c); } } }