RowPresenter.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 android.view.View; 17import android.view.ViewGroup; 18 19/** 20 * In addition to {@link Presenter} which defines how to render and bind data to row view, 21 * RowPresenter receives calls from upper level(typically a fragment) when: 22 * <ul> 23 * <li> 24 * Row is selected via {@link #setRowViewSelected(ViewHolder, boolean)}. The event 25 * is triggered immediately when there is a row selection change before the selection 26 * animation is started. 27 * Subclass of RowPresenter may override and add more works in 28 * {@link #onRowViewSelected(ViewHolder, boolean)}. 29 * </li> 30 * <li> 31 * Row is expanded to full width via {@link #setRowViewExpanded(ViewHolder, boolean)}. 32 * The event is triggered immediately before the expand animation is started. 33 * Subclass of RowPresenter may override and add more works in 34 * {@link #onRowViewExpanded(ViewHolder, boolean)}. 35 * </li> 36 * </ul> 37 * </p> 38 * Subclass of RowPresenter may add more fields in ViewHolder by overriding 39 * {@link #createRowViewHolder(ViewGroup)} and {@link #initializeRowViewHolder(ViewHolder)}. 40 * </p> 41 * <p> 42 * RowPresenter contains an optional and replaceable {@link RowHeaderPresenter} that 43 * renders header. User can disable default rendering or replace with a new header presenter 44 * by calling {@link #setHeaderPresenter(RowHeaderPresenter)}. 45 * </p> 46 * <p> 47 * Provides {@link OnItemSelectedListener} and {@link OnItemClickedListener}. 48 * </p> 49 */ 50public abstract class RowPresenter extends Presenter { 51 52 public static class ViewHolder extends Presenter.ViewHolder { 53 RowHeaderPresenter.ViewHolder mHeaderViewHolder; 54 Row mRow; 55 boolean mSelected; 56 boolean mExpanded; 57 boolean mInitialzed; 58 float mSelectLevel = 0f; // initially unselected 59 public ViewHolder(View view) { 60 super(view); 61 } 62 public final Row getRow() { 63 return mRow; 64 } 65 public final boolean isExpanded() { 66 return mExpanded; 67 } 68 public final boolean isSelected() { 69 return mSelected; 70 } 71 public final float getSelectLevel() { 72 return mSelectLevel; 73 } 74 public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() { 75 return mHeaderViewHolder; 76 } 77 } 78 79 private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter(); 80 private OnItemSelectedListener mOnItemSelectedListener; 81 private OnItemClickedListener mOnItemClickedListener; 82 83 boolean mSelectEffectEnabled = true; 84 85 @Override 86 public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { 87 ViewHolder vh = createRowViewHolder(parent); 88 vh.mInitialzed = false; 89 initializeRowViewHolder(vh); 90 if (!vh.mInitialzed) { 91 throw new RuntimeException("super.initializeRowViewHolder() must be called"); 92 } 93 return vh; 94 } 95 96 /** 97 * Called to create a ViewHolder object for row, subclass of {@link RowPresenter} 98 * should override and return a different concrete ViewHolder object. 99 */ 100 protected abstract ViewHolder createRowViewHolder(ViewGroup parent); 101 102 /** 103 * Called after a {@link RowPresenter.ViewHolder} is created, 104 * subclass of {@link RowPresenter} may override this method and start with calling 105 * super.initializeRowViewHolder(ViewHolder). 106 */ 107 protected void initializeRowViewHolder(ViewHolder vh) { 108 if (mHeaderPresenter != null) { 109 vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder) 110 mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view); 111 ((ViewGroup) vh.view).addView(vh.mHeaderViewHolder.view, 0); 112 } 113 vh.mInitialzed = true; 114 } 115 116 /** 117 * Change the presenter used for rendering header. Can be null to disable header rendering. 118 * The method must be called before creating any row view. 119 */ 120 public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) { 121 mHeaderPresenter = headerPresenter; 122 } 123 124 /** 125 * Get optional presenter used for rendering header. May return null. 126 */ 127 public final RowHeaderPresenter getHeaderPresenter() { 128 return mHeaderPresenter; 129 } 130 131 /** 132 * Change expanded state of row view. 133 */ 134 public final void setRowViewExpanded(ViewHolder holder, boolean expanded) { 135 holder.mExpanded = expanded; 136 onRowViewExpanded(holder, expanded); 137 } 138 139 /** 140 * Change select state of row view. 141 */ 142 public final void setRowViewSelected(ViewHolder holder, boolean selected) { 143 holder.mSelected = selected; 144 onRowViewSelected(holder, selected); 145 } 146 147 /** 148 * Subclass may override and respond to expanded state change of row in fragment. 149 * Default implementation hide/show header view depending on expanded state. 150 * Subclass may make visual changes to row view but not allowed to create 151 * animation on the row view. 152 */ 153 protected void onRowViewExpanded(ViewHolder vh, boolean expanded) { 154 if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) { 155 mHeaderPresenter.setHidden(vh.mHeaderViewHolder, !expanded); 156 } 157 } 158 159 /** 160 * Subclass may override and respond to event Row is selected. 161 * Subclass may make visual changes to row view but not allowed to create 162 * animation on the row view. 163 */ 164 protected void onRowViewSelected(ViewHolder vh, boolean selected) { 165 if (selected && mOnItemSelectedListener != null) { 166 mOnItemSelectedListener.onItemSelected(null, vh.getRow()); 167 } 168 } 169 170 /** 171 * Set current select level from 0(unselected) to 1(selected). 172 * Subclass should override {@link #onSelectLevelChanged(ViewHolder)}. 173 */ 174 public final void setSelectLevel(ViewHolder vh, float level) { 175 vh.mSelectLevel = level; 176 onSelectLevelChanged(vh); 177 } 178 179 /** 180 * Get current select level from 0(unselected) to 1(selected). 181 */ 182 public final float getSelectLevel(ViewHolder vh) { 183 return vh.mSelectLevel; 184 } 185 186 /** 187 * Callback when select level is changed. Default implementation applies select level 188 * to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)} 189 * when {@link #getSelectEffectEnabled()} is true. 190 * Subclass may override this function and implements its own select effect. When it 191 * overrides, it should also override {@link #isUsingDefaultSelectEffect()} to disable 192 * the default dimming effect applied by framework. 193 */ 194 protected void onSelectLevelChanged(ViewHolder vh) { 195 if (getSelectEffectEnabled() && vh.mHeaderViewHolder != null) { 196 mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel); 197 } 198 } 199 200 /** 201 * Enables or disables the row selection effect. 202 * This is not only for enable/disable default dim implementation but also subclass must 203 * respect this flag. 204 */ 205 public final void setSelectEffectEnabled(boolean applyDimOnSelect) { 206 mSelectEffectEnabled = applyDimOnSelect; 207 } 208 209 /** 210 * Returns true if row selection effect is enabled. 211 * This is not only for enable/disable default dim implementation but also subclass must 212 * respect this flag. 213 */ 214 public final boolean getSelectEffectEnabled() { 215 return mSelectEffectEnabled; 216 } 217 218 /** 219 * Return if using default dimming effect provided by framework (fragment). Subclass 220 * may(most likely) return false and override {@link #onSelectLevelChanged(ViewHolder)}. 221 */ 222 public boolean isUsingDefaultSelectEffect() { 223 return true; 224 } 225 226 @Override 227 public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 228 ViewHolder vh = (ViewHolder) viewHolder; 229 vh.mRow = (Row) item; 230 if (vh.mHeaderViewHolder != null) { 231 mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item); 232 } 233 vh.mSelected = false; 234 vh.mExpanded = false; 235 onRowViewExpanded(vh, false); 236 onRowViewSelected(vh, false); 237 } 238 239 @Override 240 public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { 241 ViewHolder vh = (ViewHolder) viewHolder; 242 if (vh.mHeaderViewHolder != null) { 243 mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder); 244 } 245 vh.mRow = null; 246 } 247 248 @Override 249 public void onViewAttachedToWindow(Presenter.ViewHolder holder) { 250 ViewHolder vh = (ViewHolder) holder; 251 if (vh.mHeaderViewHolder != null) { 252 mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder); 253 } 254 } 255 256 @Override 257 public void onViewDetachedFromWindow(Presenter.ViewHolder holder) { 258 ViewHolder vh = (ViewHolder) holder; 259 if (vh.mHeaderViewHolder != null) { 260 mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder); 261 } 262 } 263 264 /** 265 * Set listener for item or row selection. RowPresenter fires row selection 266 * event with null item, subclass of RowPresenter e.g. {@link ListRowPresenter} can 267 * fire a selection event with selected item. 268 */ 269 public final void setOnItemSelectedListener(OnItemSelectedListener listener) { 270 mOnItemSelectedListener = listener; 271 } 272 273 /** 274 * Get listener for item or row selection. 275 */ 276 public final OnItemSelectedListener getOnItemSelectedListener() { 277 return mOnItemSelectedListener; 278 } 279 280 /** 281 * Set listener for item click event. RowPresenter does nothing but subclass of 282 * RowPresenter may fire item click event if it does have a concept of item. 283 * OnItemClickedListener will override the listener that item presenter sets during 284 * {@link Presenter#onCreateViewHolder(ViewGroup)}. So in general, developer should 285 * choose one of the listeners but not both. 286 */ 287 public final void setOnItemClickedListener(OnItemClickedListener listener) { 288 mOnItemClickedListener = listener; 289 } 290 291 /** 292 * Set listener for item click event. 293 */ 294 public final OnItemClickedListener getOnItemClickedListener() { 295 return mOnItemClickedListener; 296 } 297} 298