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