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 */ 14 package android.support.v17.leanback.widget; 15 16 import android.support.v17.leanback.app.HeadersFragment; 17 import android.support.v17.leanback.graphics.ColorOverlayDimmer; 18 import android.view.View; 19 import android.view.ViewGroup; 20 21 /** 22 * An abstract {@link Presenter} that renders an Object in RowsFragment, the object can be 23 * subclass {@link Row} or a generic one. When the object is not {@link Row} class, 24 * {@link ViewHolder#getRow()} returns null. 25 * 26 * <h3>Customize UI widgets</h3> 27 * When a subclass of RowPresenter adds UI widgets, it should subclass 28 * {@link RowPresenter.ViewHolder} and override {@link #createRowViewHolder(ViewGroup)} 29 * and {@link #initializeRowViewHolder(ViewHolder)}. The subclass must use layout id 30 * "row_content" for the widget that will be aligned to the title of any {@link HeadersFragment} 31 * that may exist in the parent fragment. RowPresenter contains an optional and 32 * replaceable {@link RowHeaderPresenter} that renders the header. You can disable 33 * the default rendering or replace the Presenter with a new header presenter 34 * by calling {@link #setHeaderPresenter(RowHeaderPresenter)}. 35 * 36 * <h3>UI events from fragments</h3> 37 * RowPresenter receives calls from its parent (typically a Fragment) when: 38 * <ul> 39 * <li> 40 * A row is selected via {@link #setRowViewSelected(Presenter.ViewHolder, boolean)}. The event 41 * is triggered immediately when there is a row selection change before the selection 42 * animation is started. Selected status may control activated status of the row (see 43 * "Activated status" below). 44 * Subclasses of RowPresenter may override {@link #onRowViewSelected(ViewHolder, boolean)}. 45 * </li> 46 * <li> 47 * A row is expanded to full height via {@link #setRowViewExpanded(Presenter.ViewHolder, boolean)} 48 * when BrowseFragment hides fast lane on the left. 49 * The event is triggered immediately before the expand animation is started. 50 * Row title is shown when row is expanded. Expanded status may control activated status 51 * of the row (see "Activated status" below). 52 * Subclasses of RowPresenter may override {@link #onRowViewExpanded(ViewHolder, boolean)}. 53 * </li> 54 * </ul> 55 * 56 * <h3>Activated status</h3> 57 * The activated status of a row is applied to the row view and its children via 58 * {@link View#setActivated(boolean)}. 59 * The activated status is typically used to control {@link BaseCardView} info region visibility. 60 * The row's activated status can be controlled by selected status and/or expanded status. 61 * Call {@link #setSyncActivatePolicy(int)} and choose one of the four policies: 62 * <ul> 63 * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED} Activated status is synced with row expanded status</li> 64 * <li>{@link #SYNC_ACTIVATED_TO_SELECTED} Activated status is synced with row selected status</li> 65 * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED} Activated status is set to true 66 * when both expanded and selected status are true</li> 67 * <li>{@link #SYNC_ACTIVATED_CUSTOM} Activated status is not controlled by selected status 68 * or expanded status, application can control activated status by its own. 69 * Application should call {@link RowPresenter.ViewHolder#setActivated(boolean)} to change 70 * activated status of row view. 71 * </li> 72 * </ul> 73 * 74 * <h3>User events</h3> 75 * RowPresenter provides {@link OnItemViewSelectedListener} and {@link OnItemViewClickedListener}. 76 * If a subclass wants to add its own {@link View.OnFocusChangeListener} or 77 * {@link View.OnClickListener}, it must do that in {@link #createRowViewHolder(ViewGroup)} 78 * to be properly chained by the library. Adding View listeners after 79 * {@link #createRowViewHolder(ViewGroup)} is undefined and may result in 80 * incorrect behavior by the library's listeners. 81 * 82 * <h3>Selection animation</h3> 83 * <p> 84 * When a user scrolls through rows, a fragment will initiate animation and call 85 * {@link #setSelectLevel(Presenter.ViewHolder, float)} with float value between 86 * 0 and 1. By default, the RowPresenter draws a dim overlay on top of the row 87 * view for views that are not selected. Subclasses may override this default effect 88 * by having {@link #isUsingDefaultSelectEffect()} return false and overriding 89 * {@link #onSelectLevelChanged(ViewHolder)} to apply a different selection effect. 90 * </p> 91 * <p> 92 * Call {@link #setSelectEffectEnabled(boolean)} to enable/disable the select effect, 93 * This will not only enable/disable the default dim effect but also subclasses must 94 * respect this flag as well. 95 * </p> 96 */ 97 public abstract class RowPresenter extends Presenter { 98 99 /** 100 * Don't synchronize row view activated status with selected status or expanded status, 101 * application will do its own through {@link RowPresenter.ViewHolder#setActivated(boolean)}. 102 */ 103 public static final int SYNC_ACTIVATED_CUSTOM = 0; 104 105 /** 106 * Synchronizes row view's activated status to expand status of the row view holder. 107 */ 108 public static final int SYNC_ACTIVATED_TO_EXPANDED = 1; 109 110 /** 111 * Synchronizes row view's activated status to selected status of the row view holder. 112 */ 113 public static final int SYNC_ACTIVATED_TO_SELECTED = 2; 114 115 /** 116 * Sets the row view's activated status to true when both expand and selected are true. 117 */ 118 public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3; 119 120 static class ContainerViewHolder extends Presenter.ViewHolder { 121 /** 122 * wrapped row view holder 123 */ 124 final ViewHolder mRowViewHolder; 125 ContainerViewHolder(RowContainerView containerView, ViewHolder rowViewHolder)126 public ContainerViewHolder(RowContainerView containerView, ViewHolder rowViewHolder) { 127 super(containerView); 128 containerView.addRowView(rowViewHolder.view); 129 if (rowViewHolder.mHeaderViewHolder != null) { 130 containerView.addHeaderView(rowViewHolder.mHeaderViewHolder.view); 131 } 132 mRowViewHolder = rowViewHolder; 133 mRowViewHolder.mContainerViewHolder = this; 134 } 135 } 136 137 /** 138 * A ViewHolder for a {@link Row}. 139 */ 140 public static class ViewHolder extends Presenter.ViewHolder { 141 private static final int ACTIVATED_NOT_ASSIGNED = 0; 142 private static final int ACTIVATED = 1; 143 private static final int NOT_ACTIVATED = 2; 144 145 ContainerViewHolder mContainerViewHolder; 146 RowHeaderPresenter.ViewHolder mHeaderViewHolder; 147 Row mRow; 148 Object mRowObject; 149 int mActivated = ACTIVATED_NOT_ASSIGNED; 150 boolean mSelected; 151 boolean mExpanded; 152 boolean mInitialzed; 153 float mSelectLevel = 0f; // initially unselected 154 protected final ColorOverlayDimmer mColorDimmer; 155 private View.OnKeyListener mOnKeyListener; 156 BaseOnItemViewSelectedListener mOnItemViewSelectedListener; 157 private BaseOnItemViewClickedListener mOnItemViewClickedListener; 158 159 /** 160 * Constructor for ViewHolder. 161 * 162 * @param view The View bound to the Row. 163 */ ViewHolder(View view)164 public ViewHolder(View view) { 165 super(view); 166 mColorDimmer = ColorOverlayDimmer.createDefault(view.getContext()); 167 } 168 169 /** 170 * Returns the row bound to this ViewHolder. Returns null if the row is not an instance of 171 * {@link Row}. 172 * @return The row bound to this ViewHolder. Returns null if the row is not an instance of 173 * {@link Row}. 174 */ getRow()175 public final Row getRow() { 176 return mRow; 177 } 178 179 /** 180 * Returns the Row object bound to this ViewHolder. 181 * @return The row object bound to this ViewHolder. 182 */ getRowObject()183 public final Object getRowObject() { 184 return mRowObject; 185 } 186 187 /** 188 * Returns whether the Row is in its expanded state. 189 * 190 * @return true if the Row is expanded, false otherwise. 191 */ isExpanded()192 public final boolean isExpanded() { 193 return mExpanded; 194 } 195 196 /** 197 * Returns whether the Row is selected. 198 * 199 * @return true if the Row is selected, false otherwise. 200 */ isSelected()201 public final boolean isSelected() { 202 return mSelected; 203 } 204 205 /** 206 * Returns the current selection level of the Row. 207 */ getSelectLevel()208 public final float getSelectLevel() { 209 return mSelectLevel; 210 } 211 212 /** 213 * Returns the view holder for the Row header for this Row. 214 */ getHeaderViewHolder()215 public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() { 216 return mHeaderViewHolder; 217 } 218 219 /** 220 * Sets the row view's activated status. The status will be applied to children through 221 * {@link #syncActivatedStatus(View)}. Application should only call this function 222 * when {@link RowPresenter#getSyncActivatePolicy()} is 223 * {@link RowPresenter#SYNC_ACTIVATED_CUSTOM}; otherwise the value will 224 * be overwritten when expanded or selected status changes. 225 */ setActivated(boolean activated)226 public final void setActivated(boolean activated) { 227 mActivated = activated ? ACTIVATED : NOT_ACTIVATED; 228 } 229 230 /** 231 * Synchronizes the activated status of view to the last value passed through 232 * {@link RowPresenter.ViewHolder#setActivated(boolean)}. No operation if 233 * {@link RowPresenter.ViewHolder#setActivated(boolean)} is never called. Normally 234 * application does not need to call this method, {@link ListRowPresenter} automatically 235 * calls this method when a child is attached to list row. However if 236 * application writes its own custom RowPresenter, it should call this method 237 * when attaches a child to the row view. 238 */ syncActivatedStatus(View view)239 public final void syncActivatedStatus(View view) { 240 if (mActivated == ACTIVATED) { 241 view.setActivated(true); 242 } else if (mActivated == NOT_ACTIVATED) { 243 view.setActivated(false); 244 } 245 } 246 247 /** 248 * Sets a key listener. 249 */ setOnKeyListener(View.OnKeyListener keyListener)250 public void setOnKeyListener(View.OnKeyListener keyListener) { 251 mOnKeyListener = keyListener; 252 } 253 254 /** 255 * Returns the key listener. 256 */ getOnKeyListener()257 public View.OnKeyListener getOnKeyListener() { 258 return mOnKeyListener; 259 } 260 261 /** 262 * Sets the listener for item or row selection. RowPresenter fires row selection 263 * event with null item. A subclass of RowPresenter e.g. {@link ListRowPresenter} may 264 * fire a selection event with selected item. 265 */ setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener)266 public final void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) { 267 mOnItemViewSelectedListener = listener; 268 } 269 270 /** 271 * Returns the listener for item or row selection. 272 */ getOnItemViewSelectedListener()273 public final BaseOnItemViewSelectedListener getOnItemViewSelectedListener() { 274 return mOnItemViewSelectedListener; 275 } 276 277 /** 278 * Sets the listener for item click event. RowPresenter does nothing but subclass of 279 * RowPresenter may fire item click event if it has the concept of item. 280 * OnItemViewClickedListener will override {@link View.OnClickListener} that 281 * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. 282 */ setOnItemViewClickedListener(BaseOnItemViewClickedListener listener)283 public final void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) { 284 mOnItemViewClickedListener = listener; 285 } 286 287 /** 288 * Returns the listener for item click event. 289 */ getOnItemViewClickedListener()290 public final BaseOnItemViewClickedListener getOnItemViewClickedListener() { 291 return mOnItemViewClickedListener; 292 } 293 /** 294 * Return {@link ViewHolder} of currently selected item inside a row ViewHolder. 295 * @return The selected item's ViewHolder. 296 */ getSelectedItemViewHolder()297 public Presenter.ViewHolder getSelectedItemViewHolder() { 298 return null; 299 } 300 301 /** 302 * Return currently selected item inside a row ViewHolder. 303 * @return The selected item. 304 */ getSelectedItem()305 public Object getSelectedItem() { 306 return null; 307 } 308 } 309 310 private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter(); 311 312 boolean mSelectEffectEnabled = true; 313 int mSyncActivatePolicy = SYNC_ACTIVATED_TO_EXPANDED; 314 315 316 /** 317 * Constructs a RowPresenter. 318 */ RowPresenter()319 public RowPresenter() { 320 mHeaderPresenter.setNullItemVisibilityGone(true); 321 } 322 323 @Override onCreateViewHolder(ViewGroup parent)324 public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { 325 ViewHolder vh = createRowViewHolder(parent); 326 vh.mInitialzed = false; 327 Presenter.ViewHolder result; 328 if (needsRowContainerView()) { 329 RowContainerView containerView = new RowContainerView(parent.getContext()); 330 if (mHeaderPresenter != null) { 331 vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder) 332 mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view); 333 } 334 result = new ContainerViewHolder(containerView, vh); 335 } else { 336 result = vh; 337 } 338 initializeRowViewHolder(vh); 339 if (!vh.mInitialzed) { 340 throw new RuntimeException("super.initializeRowViewHolder() must be called"); 341 } 342 return result; 343 } 344 345 /** 346 * Called to create a ViewHolder object for a Row. Subclasses will override 347 * this method to return a different concrete ViewHolder object. 348 * 349 * @param parent The parent View for the Row's view holder. 350 * @return A ViewHolder for the Row's View. 351 */ createRowViewHolder(ViewGroup parent)352 protected abstract ViewHolder createRowViewHolder(ViewGroup parent); 353 354 /** 355 * Returns true if the Row view should clip its children. The clipChildren 356 * flag is set on view in {@link #initializeRowViewHolder(ViewHolder)}. Note that 357 * Slide transition or explode transition need turn off clipChildren. 358 * Default value is false. 359 */ isClippingChildren()360 protected boolean isClippingChildren() { 361 return false; 362 } 363 364 /** 365 * Called after a {@link RowPresenter.ViewHolder} is created for a Row. 366 * Subclasses may override this method and start by calling 367 * super.initializeRowViewHolder(ViewHolder). 368 * 369 * @param vh The ViewHolder to initialize for the Row. 370 */ initializeRowViewHolder(ViewHolder vh)371 protected void initializeRowViewHolder(ViewHolder vh) { 372 vh.mInitialzed = true; 373 if (!isClippingChildren()) { 374 // set clip children to false for slide transition 375 if (vh.view instanceof ViewGroup) { 376 ((ViewGroup) vh.view).setClipChildren(false); 377 } 378 if (vh.mContainerViewHolder != null) { 379 ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false); 380 } 381 } 382 } 383 384 /** 385 * Sets the Presenter used for rendering the header. Can be null to disable 386 * header rendering. The method must be called before creating any Row Views. 387 */ setHeaderPresenter(RowHeaderPresenter headerPresenter)388 public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) { 389 mHeaderPresenter = headerPresenter; 390 } 391 392 /** 393 * Returns the Presenter used for rendering the header, or null if none has been 394 * set. 395 */ getHeaderPresenter()396 public final RowHeaderPresenter getHeaderPresenter() { 397 return mHeaderPresenter; 398 } 399 400 /** 401 * Returns the {@link RowPresenter.ViewHolder} from the given RowPresenter 402 * ViewHolder. 403 */ getRowViewHolder(Presenter.ViewHolder holder)404 public final ViewHolder getRowViewHolder(Presenter.ViewHolder holder) { 405 if (holder instanceof ContainerViewHolder) { 406 return ((ContainerViewHolder) holder).mRowViewHolder; 407 } else { 408 return (ViewHolder) holder; 409 } 410 } 411 412 /** 413 * Sets the expanded state of a Row view. 414 * 415 * @param holder The Row ViewHolder to set expanded state on. 416 * @param expanded True if the Row is expanded, false otherwise. 417 */ setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded)418 public final void setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded) { 419 ViewHolder rowViewHolder = getRowViewHolder(holder); 420 rowViewHolder.mExpanded = expanded; 421 onRowViewExpanded(rowViewHolder, expanded); 422 } 423 424 /** 425 * Sets the selected state of a Row view. 426 * 427 * @param holder The Row ViewHolder to set expanded state on. 428 * @param selected True if the Row is expanded, false otherwise. 429 */ setRowViewSelected(Presenter.ViewHolder holder, boolean selected)430 public final void setRowViewSelected(Presenter.ViewHolder holder, boolean selected) { 431 ViewHolder rowViewHolder = getRowViewHolder(holder); 432 rowViewHolder.mSelected = selected; 433 onRowViewSelected(rowViewHolder, selected); 434 } 435 436 /** 437 * Called when the row view's expanded state changes. A subclass may override this method to 438 * respond to expanded state changes of a Row. 439 * The default implementation will hide/show the header view. Subclasses may 440 * make visual changes to the Row View but must not create animation on the 441 * Row view. 442 */ onRowViewExpanded(ViewHolder vh, boolean expanded)443 protected void onRowViewExpanded(ViewHolder vh, boolean expanded) { 444 updateHeaderViewVisibility(vh); 445 updateActivateStatus(vh, vh.view); 446 } 447 448 /** 449 * Updates the view's activate status according to {@link #getSyncActivatePolicy()} and the 450 * selected status and expanded status of the RowPresenter ViewHolder. 451 */ updateActivateStatus(ViewHolder vh, View view)452 private void updateActivateStatus(ViewHolder vh, View view) { 453 switch (mSyncActivatePolicy) { 454 case SYNC_ACTIVATED_TO_EXPANDED: 455 vh.setActivated(vh.isExpanded()); 456 break; 457 case SYNC_ACTIVATED_TO_SELECTED: 458 vh.setActivated(vh.isSelected()); 459 break; 460 case SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED: 461 vh.setActivated(vh.isExpanded() && vh.isSelected()); 462 break; 463 } 464 vh.syncActivatedStatus(view); 465 } 466 467 /** 468 * Sets the policy of updating row view activated status. Can be one of: 469 * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED} 470 * <li> {@link #SYNC_ACTIVATED_TO_SELECTED} 471 * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED} 472 * <li> {@link #SYNC_ACTIVATED_CUSTOM} 473 */ setSyncActivatePolicy(int syncActivatePolicy)474 public final void setSyncActivatePolicy(int syncActivatePolicy) { 475 mSyncActivatePolicy = syncActivatePolicy; 476 } 477 478 /** 479 * Returns the policy of updating row view activated status. Can be one of: 480 * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED} 481 * <li> {@link #SYNC_ACTIVATED_TO_SELECTED} 482 * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED} 483 * <li> {@link #SYNC_ACTIVATED_CUSTOM} 484 */ getSyncActivatePolicy()485 public final int getSyncActivatePolicy() { 486 return mSyncActivatePolicy; 487 } 488 489 /** 490 * This method is only called from 491 * {@link #onRowViewSelected(ViewHolder, boolean)} onRowViewSelected. 492 * The default behavior is to signal row selected events with a null item parameter. 493 * A Subclass of RowPresenter having child items should override this method and dispatch 494 * events with item information. 495 */ dispatchItemSelectedListener(ViewHolder vh, boolean selected)496 protected void dispatchItemSelectedListener(ViewHolder vh, boolean selected) { 497 if (selected) { 498 if (vh.mOnItemViewSelectedListener != null) { 499 vh.mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRowObject()); 500 } 501 } 502 } 503 504 /** 505 * Called when the given row view changes selection state. A subclass may override this to 506 * respond to selected state changes of a Row. A subclass may make visual changes to Row view 507 * but must not create animation on the Row view. 508 */ onRowViewSelected(ViewHolder vh, boolean selected)509 protected void onRowViewSelected(ViewHolder vh, boolean selected) { 510 dispatchItemSelectedListener(vh, selected); 511 updateHeaderViewVisibility(vh); 512 updateActivateStatus(vh, vh.view); 513 } 514 updateHeaderViewVisibility(ViewHolder vh)515 private void updateHeaderViewVisibility(ViewHolder vh) { 516 if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) { 517 RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view); 518 containerView.showHeader(vh.isExpanded()); 519 } 520 } 521 522 /** 523 * Sets the current select level to a value between 0 (unselected) and 1 (selected). 524 * Subclasses may override {@link #onSelectLevelChanged(ViewHolder)} to 525 * respond to changes in the selected level. 526 */ setSelectLevel(Presenter.ViewHolder vh, float level)527 public final void setSelectLevel(Presenter.ViewHolder vh, float level) { 528 ViewHolder rowViewHolder = getRowViewHolder(vh); 529 rowViewHolder.mSelectLevel = level; 530 onSelectLevelChanged(rowViewHolder); 531 } 532 533 /** 534 * Returns the current select level. The value will be between 0 (unselected) 535 * and 1 (selected). 536 */ getSelectLevel(Presenter.ViewHolder vh)537 public final float getSelectLevel(Presenter.ViewHolder vh) { 538 return getRowViewHolder(vh).mSelectLevel; 539 } 540 541 /** 542 * Callback when the select level changes. The default implementation applies 543 * the select level to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)} 544 * when {@link #getSelectEffectEnabled()} is true. Subclasses may override 545 * this function and implement a different select effect. In this case, 546 * the method {@link #isUsingDefaultSelectEffect()} should also be overridden to disable 547 * the default dimming effect. 548 */ onSelectLevelChanged(ViewHolder vh)549 protected void onSelectLevelChanged(ViewHolder vh) { 550 if (getSelectEffectEnabled()) { 551 vh.mColorDimmer.setActiveLevel(vh.mSelectLevel); 552 if (vh.mHeaderViewHolder != null) { 553 mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel); 554 } 555 if (isUsingDefaultSelectEffect()) { 556 ((RowContainerView) vh.mContainerViewHolder.view).setForegroundColor( 557 vh.mColorDimmer.getPaint().getColor()); 558 } 559 } 560 } 561 562 /** 563 * Enables or disables the row selection effect. 564 * This will not only affect the default dim effect, but subclasses must 565 * respect this flag as well. 566 */ setSelectEffectEnabled(boolean applyDimOnSelect)567 public final void setSelectEffectEnabled(boolean applyDimOnSelect) { 568 mSelectEffectEnabled = applyDimOnSelect; 569 } 570 571 /** 572 * Returns true if the row selection effect is enabled. 573 * This value not only determines whether the default dim implementation is 574 * used, but subclasses must also respect this flag. 575 */ getSelectEffectEnabled()576 public final boolean getSelectEffectEnabled() { 577 return mSelectEffectEnabled; 578 } 579 580 /** 581 * Returns true if this RowPresenter is using the default dimming effect. 582 * A subclass may (most likely) return false and 583 * override {@link #onSelectLevelChanged(ViewHolder)}. 584 */ isUsingDefaultSelectEffect()585 public boolean isUsingDefaultSelectEffect() { 586 return true; 587 } 588 needsDefaultSelectEffect()589 final boolean needsDefaultSelectEffect() { 590 return isUsingDefaultSelectEffect() && getSelectEffectEnabled(); 591 } 592 needsRowContainerView()593 final boolean needsRowContainerView() { 594 return mHeaderPresenter != null || needsDefaultSelectEffect(); 595 } 596 597 @Override onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)598 public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 599 onBindRowViewHolder(getRowViewHolder(viewHolder), item); 600 } 601 602 /** 603 * Binds the given row object to the given ViewHolder. 604 * Derived classes of {@link RowPresenter} overriding 605 * {@link #onBindRowViewHolder(ViewHolder, Object)} must call through the super class's 606 * implementation of this method. 607 */ onBindRowViewHolder(ViewHolder vh, Object item)608 protected void onBindRowViewHolder(ViewHolder vh, Object item) { 609 vh.mRowObject = item; 610 vh.mRow = item instanceof Row ? (Row) item : null; 611 if (vh.mHeaderViewHolder != null && vh.getRow() != null) { 612 mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item); 613 } 614 } 615 616 @Override onUnbindViewHolder(Presenter.ViewHolder viewHolder)617 public final void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { 618 onUnbindRowViewHolder(getRowViewHolder(viewHolder)); 619 } 620 621 /** 622 * Unbinds the given ViewHolder. 623 * Derived classes of {@link RowPresenter} overriding {@link #onUnbindRowViewHolder(ViewHolder)} 624 * must call through the super class's implementation of this method. 625 */ onUnbindRowViewHolder(ViewHolder vh)626 protected void onUnbindRowViewHolder(ViewHolder vh) { 627 if (vh.mHeaderViewHolder != null) { 628 mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder); 629 } 630 vh.mRow = null; 631 vh.mRowObject = null; 632 } 633 634 @Override onViewAttachedToWindow(Presenter.ViewHolder holder)635 public final void onViewAttachedToWindow(Presenter.ViewHolder holder) { 636 onRowViewAttachedToWindow(getRowViewHolder(holder)); 637 } 638 639 /** 640 * Invoked when the row view is attached to the window. 641 */ onRowViewAttachedToWindow(ViewHolder vh)642 protected void onRowViewAttachedToWindow(ViewHolder vh) { 643 if (vh.mHeaderViewHolder != null) { 644 mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder); 645 } 646 } 647 648 @Override onViewDetachedFromWindow(Presenter.ViewHolder holder)649 public final void onViewDetachedFromWindow(Presenter.ViewHolder holder) { 650 onRowViewDetachedFromWindow(getRowViewHolder(holder)); 651 } 652 653 /** 654 * Invoked when the row view is detached from the window. 655 */ onRowViewDetachedFromWindow(ViewHolder vh)656 protected void onRowViewDetachedFromWindow(ViewHolder vh) { 657 if (vh.mHeaderViewHolder != null) { 658 mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder); 659 } 660 cancelAnimationsRecursive(vh.view); 661 } 662 663 /** 664 * Freezes/unfreezes the row, typically used when a transition starts/ends. 665 * This method is called by the fragment, it should not call it directly by the application. 666 */ freeze(ViewHolder holder, boolean freeze)667 public void freeze(ViewHolder holder, boolean freeze) { 668 } 669 670 /** 671 * Changes the visibility of views. The entrance transition will be run against the views that 672 * change visibilities. A subclass may override and begin with calling 673 * super.setEntranceTransitionState(). This method is called by the fragment, 674 * it should not be called directly by the application. 675 * 676 * @param holder The ViewHolder of the row. 677 * @param afterEntrance true if children of row participating in entrance transition 678 * should be set to visible, false otherwise. 679 */ setEntranceTransitionState(ViewHolder holder, boolean afterEntrance)680 public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) { 681 if (holder.mHeaderViewHolder != null 682 && holder.mHeaderViewHolder.view.getVisibility() != View.GONE) { 683 holder.mHeaderViewHolder.view.setVisibility(afterEntrance 684 ? View.VISIBLE : View.INVISIBLE); 685 } 686 } 687 } 688