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