ListRowPresenter.java revision cf94c5fa8ae8edb7e26a623133207415ceeed187
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14package android.support.v17.leanback.widget; 15 16import java.util.ArrayList; 17 18import android.graphics.Canvas; 19import android.support.v17.leanback.graphics.ColorOverlayDimmer; 20import android.support.v17.leanback.widget.Presenter.ViewHolder; 21import android.support.v7.widget.RecyclerView; 22import android.view.View; 23import android.view.ViewGroup; 24 25/** 26 * Presents a ListRow using a {@link HorizontalGridView} hosted in a {@link BrowseRowView}. 27 * <p> 28 * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to 29 * display a view for the currently focused list item below the rendered 30 * list. This view is known as a hover card. 31 * </p> 32 * <p> 33 * ListRowPresenter has the same capability of {@link #setHeaderPresenter(RowHeaderPresenter)} 34 * as parent class {@link RowPresenter}. 35 * </p> 36 */ 37public class ListRowPresenter extends RowPresenter { 38 39 private static final String TAG = "ListRowPresenter"; 40 private static final boolean DEBUG = false; 41 42 public static class ViewHolder extends RowPresenter.ViewHolder { 43 final ListRowPresenter mListRowPresenter; 44 final HorizontalGridView mGridView; 45 final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter(); 46 final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher(); 47 final ColorOverlayDimmer mColorDimmer; 48 49 public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) { 50 super(rootView); 51 mGridView = gridView; 52 mListRowPresenter = p; 53 mColorDimmer = ColorOverlayDimmer.createDefault(rootView.getContext()); 54 } 55 56 public final ListRowPresenter getListRowPresenter() { 57 return mListRowPresenter; 58 } 59 60 public final HorizontalGridView getGridView() { 61 return mGridView; 62 } 63 } 64 65 private PresenterSelector mHoverCardPresenterSelector; 66 67 @Override 68 protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) { 69 super.initializeRowViewHolder(holder); 70 final ViewHolder rowViewHolder = (ViewHolder) holder; 71 if (needsDefaultListItemDecoration()) { 72 rowViewHolder.mGridView.addItemDecoration(new ItemDecoration(rowViewHolder)); 73 } 74 FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter); 75 rowViewHolder.mGridView.setOnChildSelectedListener( 76 new OnChildSelectedListener() { 77 @Override 78 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 79 selectChildView(rowViewHolder, view); 80 } 81 }); 82 if (getOnItemClickedListener() != null) { 83 // Only when having an OnItemClickListner, we will attach the OnClickListener. 84 rowViewHolder.mItemBridgeAdapter.setAdapterListener( 85 new ItemBridgeAdapter.AdapterListener() { 86 @Override 87 public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { 88 viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() { 89 @Override 90 public void onClick(View v) { 91 ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) 92 rowViewHolder.mGridView.getChildViewHolder(v); 93 if (getOnItemClickedListener() != null) { 94 getOnItemClickedListener().onItemClicked(ibh.mItem, 95 (ListRow) rowViewHolder.mRow); 96 } 97 } 98 }); 99 } 100 }); 101 } 102 } 103 104 private boolean needsDefaultListItemDecoration() { 105 return isUsingDefaultListSelectEffect() && getSelectEffectEnabled(); 106 } 107 108 /** 109 * Set {@link PresenterSelector} used for showing a select object in a hover card. 110 */ 111 public final void setHoverCardPresenterSelector(PresenterSelector selector) { 112 mHoverCardPresenterSelector = selector; 113 } 114 115 /** 116 * Get {@link PresenterSelector} used for showing a select object in a hover card. 117 */ 118 public final PresenterSelector getHoverCardPresenterSelector() { 119 return mHoverCardPresenterSelector; 120 } 121 122 /* 123 * Perform operations when a child of horizontal grid view is selected. 124 */ 125 private void selectChildView(ViewHolder rowViewHolder, View view) { 126 ItemBridgeAdapter.ViewHolder ibh = null; 127 if (view != null) { 128 ibh = (ItemBridgeAdapter.ViewHolder) 129 rowViewHolder.mGridView.getChildViewHolder(view); 130 } 131 if (view == null) { 132 if (mHoverCardPresenterSelector != null) { 133 rowViewHolder.mHoverCardViewSwitcher.unselect(); 134 } 135 if (getOnItemSelectedListener() != null) { 136 getOnItemSelectedListener().onItemSelected(null, rowViewHolder.mRow); 137 } 138 } else if (rowViewHolder.mExpanded && rowViewHolder.mSelected) { 139 if (mHoverCardPresenterSelector != null) { 140 rowViewHolder.mHoverCardViewSwitcher.select(rowViewHolder.mGridView, view, 141 ibh.mItem); 142 } 143 if (getOnItemSelectedListener() != null) { 144 getOnItemSelectedListener().onItemSelected(ibh.mItem, rowViewHolder.mRow); 145 } 146 } 147 } 148 149 @Override 150 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 151 BrowseRowView rowView = new BrowseRowView(parent.getContext()); 152 return new ViewHolder(rowView, rowView.getGridView(), this); 153 } 154 155 @Override 156 protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) { 157 updateFooterViewSwitcher((ViewHolder) holder); 158 updateInitialChildSelection((ViewHolder) holder); 159 } 160 161 /* 162 * Show or hide hover card when row selection or expanded state is changed. 163 */ 164 private void updateFooterViewSwitcher(ViewHolder vh) { 165 if (vh.mExpanded && vh.mSelected) { 166 if (mHoverCardPresenterSelector != null) { 167 vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view, 168 mHoverCardPresenterSelector); 169 } 170 } else { 171 if (mHoverCardPresenterSelector != null) { 172 vh.mHoverCardViewSwitcher.clear(); 173 } 174 } 175 } 176 177 /* 178 * Make initial child selection when row selection state is changed. 179 */ 180 private void updateInitialChildSelection(ViewHolder vh) { 181 if (vh.mExpanded && vh.mSelected) { 182 ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) 183 vh.mGridView.findViewHolderForPosition( 184 vh.mGridView.getSelectedPosition()); 185 selectChildView(vh, ibh == null ? null : ibh.mHolder.view); 186 } else { 187 selectChildView(vh, null); 188 } 189 } 190 191 @Override 192 protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) { 193 super.onRowViewExpanded(holder, expanded); 194 ViewHolder vh = (ViewHolder) holder; 195 vh.mGridView.setClipToPadding(!expanded); 196 updateFooterViewSwitcher(vh); 197 } 198 199 @Override 200 public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { 201 super.onBindViewHolder(holder, item); 202 ViewHolder vh = (ViewHolder)holder; 203 ListRow rowItem = (ListRow) item; 204 vh.mItemBridgeAdapter.clear(); 205 vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter()); 206 vh.mGridView.setAdapter(vh.mItemBridgeAdapter); 207 } 208 209 @Override 210 public void onUnbindViewHolder(Presenter.ViewHolder holder) { 211 ViewHolder vh = (ViewHolder)holder; 212 vh.mGridView.setAdapter(null); 213 super.onUnbindViewHolder(holder); 214 } 215 216 /** 217 * ListRowPresenter overrides the default select effect of {@link RowPresenter} 218 * and return false. 219 */ 220 @Override 221 public final boolean isUsingDefaultSelectEffect() { 222 return false; 223 } 224 225 /** 226 * Returns true so that default select effect is applied to each individual 227 * child of {@link HorizontalGridView}. Subclass may return false to disable 228 * the default implementation. 229 * @see #onSelectLevelChanged(RowPresenter.ViewHolder) 230 */ 231 public boolean isUsingDefaultListSelectEffect() { 232 return true; 233 } 234 235 /** 236 * Applies select level to header and draw a default color dim over each child 237 * of {@link HorizontalGridView}. 238 * <p> 239 * Subclass may override this method. A subclass 240 * needs to call super.onSelectLevelChanged() for applying header select level 241 * and optionally applying a default select level to each child view of 242 * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()} 243 * is true. Subclass may override {@link #isUsingDefaultListSelectEffect()} to return 244 * false and deal with the individual item select level by itself. 245 * </p> 246 */ 247 @Override 248 protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) { 249 super.onSelectLevelChanged(holder); 250 if (needsDefaultListItemDecoration()) { 251 ViewHolder vh = (ViewHolder) holder; 252 vh.mColorDimmer.setActiveLevel(holder.mSelectLevel); 253 vh.mGridView.invalidate(); 254 } 255 } 256 257 private void drawDimSelectionForChildren(ViewHolder vh, Canvas c) { 258 final ColorOverlayDimmer dimmer = vh.mColorDimmer; 259 if (dimmer.needsDraw()) { 260 final HorizontalGridView gridView = vh.mGridView; 261 // Clip to padding when not expanded 262 if (!vh.mExpanded) { 263 c.clipRect(gridView.getPaddingLeft(), gridView.getPaddingTop(), 264 gridView.getWidth() - gridView.getPaddingRight(), 265 gridView.getHeight() - gridView.getPaddingBottom()); 266 } 267 for (int i = 0, count = gridView.getChildCount(); i < count; i++) { 268 dimmer.drawColorOverlay(c, gridView.getChildAt(i), true); 269 } 270 } 271 } 272 273 final class ItemDecoration extends RecyclerView.ItemDecoration { 274 ViewHolder mViewHolder; 275 ItemDecoration(ViewHolder viewHolder) { 276 mViewHolder = viewHolder; 277 } 278 @Override 279 public void onDrawOver(Canvas c, RecyclerView parent) { 280 drawDimSelectionForChildren(mViewHolder, c); 281 } 282 } 283 284} 285