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