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