• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 it's 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         private 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 
295     private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
296 
297     boolean mSelectEffectEnabled = true;
298     int mSyncActivatePolicy = SYNC_ACTIVATED_TO_EXPANDED;
299 
300 
301     /**
302      * Constructs a RowPresenter.
303      */
RowPresenter()304     public RowPresenter() {
305         mHeaderPresenter.setNullItemVisibilityGone(true);
306     }
307 
308     @Override
onCreateViewHolder(ViewGroup parent)309     public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
310         ViewHolder vh = createRowViewHolder(parent);
311         vh.mInitialzed = false;
312         Presenter.ViewHolder result;
313         if (needsRowContainerView()) {
314             RowContainerView containerView = new RowContainerView(parent.getContext());
315             if (mHeaderPresenter != null) {
316                 vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder)
317                         mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view);
318             }
319             result = new ContainerViewHolder(containerView, vh);
320         } else {
321             result = vh;
322         }
323         initializeRowViewHolder(vh);
324         if (!vh.mInitialzed) {
325             throw new RuntimeException("super.initializeRowViewHolder() must be called");
326         }
327         return result;
328     }
329 
330     /**
331      * Called to create a ViewHolder object for a Row. Subclasses will override
332      * this method to return a different concrete ViewHolder object.
333      *
334      * @param parent The parent View for the Row's view holder.
335      * @return A ViewHolder for the Row's View.
336      */
createRowViewHolder(ViewGroup parent)337     protected abstract ViewHolder createRowViewHolder(ViewGroup parent);
338 
339     /**
340      * Returns true if the Row view should clip it's children.  The clipChildren
341      * flag is set on view in {@link #initializeRowViewHolder(ViewHolder)}.  Note that
342      * Slide transition or explode transition need turn off clipChildren.
343      * Default value is false.
344      */
isClippingChildren()345     protected boolean isClippingChildren() {
346         return false;
347     }
348 
349     /**
350      * Called after a {@link RowPresenter.ViewHolder} is created for a Row.
351      * Subclasses may override this method and start by calling
352      * super.initializeRowViewHolder(ViewHolder).
353      *
354      * @param vh The ViewHolder to initialize for the Row.
355      */
initializeRowViewHolder(ViewHolder vh)356     protected void initializeRowViewHolder(ViewHolder vh) {
357         vh.mInitialzed = true;
358         if (!isClippingChildren()) {
359             // set clip children to false for slide transition
360             if (vh.view instanceof ViewGroup) {
361                 ((ViewGroup) vh.view).setClipChildren(false);
362             }
363             if (vh.mContainerViewHolder != null) {
364                 ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false);
365             }
366         }
367     }
368 
369     /**
370      * Sets the Presenter used for rendering the header. Can be null to disable
371      * header rendering. The method must be called before creating any Row Views.
372      */
setHeaderPresenter(RowHeaderPresenter headerPresenter)373     public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) {
374         mHeaderPresenter = headerPresenter;
375     }
376 
377     /**
378      * Returns the Presenter used for rendering the header, or null if none has been
379      * set.
380      */
getHeaderPresenter()381     public final RowHeaderPresenter getHeaderPresenter() {
382         return mHeaderPresenter;
383     }
384 
385     /**
386      * Returns the {@link RowPresenter.ViewHolder} from the given RowPresenter
387      * ViewHolder.
388      */
getRowViewHolder(Presenter.ViewHolder holder)389     public final ViewHolder getRowViewHolder(Presenter.ViewHolder holder) {
390         if (holder instanceof ContainerViewHolder) {
391             return ((ContainerViewHolder) holder).mRowViewHolder;
392         } else {
393             return (ViewHolder) holder;
394         }
395     }
396 
397     /**
398      * Sets the expanded state of a Row view.
399      *
400      * @param holder The Row ViewHolder to set expanded state on.
401      * @param expanded True if the Row is expanded, false otherwise.
402      */
setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded)403     public final void setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded) {
404         ViewHolder rowViewHolder = getRowViewHolder(holder);
405         rowViewHolder.mExpanded = expanded;
406         onRowViewExpanded(rowViewHolder, expanded);
407     }
408 
409     /**
410      * Sets the selected state of a Row view.
411      *
412      * @param holder The Row ViewHolder to set expanded state on.
413      * @param selected True if the Row is expanded, false otherwise.
414      */
setRowViewSelected(Presenter.ViewHolder holder, boolean selected)415     public final void setRowViewSelected(Presenter.ViewHolder holder, boolean selected) {
416         ViewHolder rowViewHolder = getRowViewHolder(holder);
417         rowViewHolder.mSelected = selected;
418         onRowViewSelected(rowViewHolder, selected);
419     }
420 
421     /**
422      * Called when the row view's expanded state changes.  A subclass may override this method to
423      * respond to expanded state changes of a Row.
424      * The default implementation will hide/show the header view. Subclasses may
425      * make visual changes to the Row View but must not create animation on the
426      * Row view.
427      */
onRowViewExpanded(ViewHolder vh, boolean expanded)428     protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
429         updateHeaderViewVisibility(vh);
430         updateActivateStatus(vh, vh.view);
431     }
432 
433     /**
434      * Updates the view's activate status according to {@link #getSyncActivatePolicy()} and the
435      * selected status and expanded status of the RowPresenter ViewHolder.
436      */
updateActivateStatus(ViewHolder vh, View view)437     private void updateActivateStatus(ViewHolder vh, View view) {
438         switch (mSyncActivatePolicy) {
439             case SYNC_ACTIVATED_TO_EXPANDED:
440                 vh.setActivated(vh.isExpanded());
441                 break;
442             case SYNC_ACTIVATED_TO_SELECTED:
443                 vh.setActivated(vh.isSelected());
444                 break;
445             case SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED:
446                 vh.setActivated(vh.isExpanded() && vh.isSelected());
447                 break;
448         }
449         vh.syncActivatedStatus(view);
450     }
451 
452     /**
453      * Sets the policy of updating row view activated status.  Can be one of:
454      * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
455      * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
456      * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
457      * <li> {@link #SYNC_ACTIVATED_CUSTOM}
458      */
setSyncActivatePolicy(int syncActivatePolicy)459     public final void setSyncActivatePolicy(int syncActivatePolicy) {
460         mSyncActivatePolicy = syncActivatePolicy;
461     }
462 
463     /**
464      * Returns the policy of updating row view activated status.  Can be one of:
465      * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
466      * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
467      * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
468      * <li> {@link #SYNC_ACTIVATED_CUSTOM}
469      */
getSyncActivatePolicy()470     public final int getSyncActivatePolicy() {
471         return mSyncActivatePolicy;
472     }
473 
474     /**
475      * This method is only called from
476      * {@link #onRowViewSelected(ViewHolder, boolean)} onRowViewSelected.
477      * The default behavior is to signal row selected events with a null item parameter.
478      * A Subclass of RowPresenter having child items should override this method and dispatch
479      * events with item information.
480      */
dispatchItemSelectedListener(ViewHolder vh, boolean selected)481     protected void dispatchItemSelectedListener(ViewHolder vh, boolean selected) {
482         if (selected) {
483             if (vh.mOnItemViewSelectedListener != null) {
484                 vh.mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRowObject());
485             }
486         }
487     }
488 
489     /**
490      * Called when the given row view changes selection state.  A subclass may override this to
491      * respond to selected state changes of a Row.  A subclass may make visual changes to Row view
492      * but must not create animation on the Row view.
493      */
onRowViewSelected(ViewHolder vh, boolean selected)494     protected void onRowViewSelected(ViewHolder vh, boolean selected) {
495         dispatchItemSelectedListener(vh, selected);
496         updateHeaderViewVisibility(vh);
497         updateActivateStatus(vh, vh.view);
498     }
499 
updateHeaderViewVisibility(ViewHolder vh)500     private void updateHeaderViewVisibility(ViewHolder vh) {
501         if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
502             RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view);
503             containerView.showHeader(vh.isExpanded());
504         }
505     }
506 
507     /**
508      * Sets the current select level to a value between 0 (unselected) and 1 (selected).
509      * Subclasses may override {@link #onSelectLevelChanged(ViewHolder)} to
510      * respond to changes in the selected level.
511      */
setSelectLevel(Presenter.ViewHolder vh, float level)512     public final void setSelectLevel(Presenter.ViewHolder vh, float level) {
513         ViewHolder rowViewHolder = getRowViewHolder(vh);
514         rowViewHolder.mSelectLevel = level;
515         onSelectLevelChanged(rowViewHolder);
516     }
517 
518     /**
519      * Returns the current select level. The value will be between 0 (unselected)
520      * and 1 (selected).
521      */
getSelectLevel(Presenter.ViewHolder vh)522     public final float getSelectLevel(Presenter.ViewHolder vh) {
523         return getRowViewHolder(vh).mSelectLevel;
524     }
525 
526     /**
527      * Callback when the select level changes. The default implementation applies
528      * the select level to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)}
529      * when {@link #getSelectEffectEnabled()} is true. Subclasses may override
530      * this function and implement a different select effect. In this case,
531      * the method {@link #isUsingDefaultSelectEffect()} should also be overridden to disable
532      * the default dimming effect.
533      */
onSelectLevelChanged(ViewHolder vh)534     protected void onSelectLevelChanged(ViewHolder vh) {
535         if (getSelectEffectEnabled()) {
536             vh.mColorDimmer.setActiveLevel(vh.mSelectLevel);
537             if (vh.mHeaderViewHolder != null) {
538                 mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel);
539             }
540             if (isUsingDefaultSelectEffect()) {
541                 ((RowContainerView) vh.mContainerViewHolder.view).setForegroundColor(
542                         vh.mColorDimmer.getPaint().getColor());
543             }
544         }
545     }
546 
547     /**
548      * Enables or disables the row selection effect.
549      * This will not only affect the default dim effect, but subclasses must
550      * respect this flag as well.
551      */
setSelectEffectEnabled(boolean applyDimOnSelect)552     public final void setSelectEffectEnabled(boolean applyDimOnSelect) {
553         mSelectEffectEnabled = applyDimOnSelect;
554     }
555 
556     /**
557      * Returns true if the row selection effect is enabled.
558      * This value not only determines whether the default dim implementation is
559      * used, but subclasses must also respect this flag.
560      */
getSelectEffectEnabled()561     public final boolean getSelectEffectEnabled() {
562         return mSelectEffectEnabled;
563     }
564 
565     /**
566      * Returns true if this RowPresenter is using the default dimming effect.
567      * A subclass may (most likely) return false and
568      * override {@link #onSelectLevelChanged(ViewHolder)}.
569      */
isUsingDefaultSelectEffect()570     public boolean isUsingDefaultSelectEffect() {
571         return true;
572     }
573 
needsDefaultSelectEffect()574     final boolean needsDefaultSelectEffect() {
575         return isUsingDefaultSelectEffect() && getSelectEffectEnabled();
576     }
577 
needsRowContainerView()578     final boolean needsRowContainerView() {
579         return mHeaderPresenter != null || needsDefaultSelectEffect();
580     }
581 
582     @Override
onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)583     public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
584         onBindRowViewHolder(getRowViewHolder(viewHolder), item);
585     }
586 
587     /**
588      * Binds the given row object to the given ViewHolder.
589      * Derived classes of {@link RowPresenter} overriding
590      * {@link #onBindRowViewHolder(ViewHolder, Object)} must call through the super class's
591      * implementation of this method.
592      */
onBindRowViewHolder(ViewHolder vh, Object item)593     protected void onBindRowViewHolder(ViewHolder vh, Object item) {
594         vh.mRowObject = item;
595         vh.mRow = item instanceof Row ? (Row) item : null;
596         if (vh.mHeaderViewHolder != null && vh.getRow() != null) {
597             mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item);
598         }
599     }
600 
601     @Override
onUnbindViewHolder(Presenter.ViewHolder viewHolder)602     public final void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
603         onUnbindRowViewHolder(getRowViewHolder(viewHolder));
604     }
605 
606     /**
607      * Unbinds the given ViewHolder.
608      * Derived classes of {@link RowPresenter} overriding {@link #onUnbindRowViewHolder(ViewHolder)}
609      * must call through the super class's implementation of this method.
610      */
onUnbindRowViewHolder(ViewHolder vh)611     protected void onUnbindRowViewHolder(ViewHolder vh) {
612         if (vh.mHeaderViewHolder != null) {
613             mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder);
614         }
615         vh.mRow = null;
616         vh.mRowObject = null;
617     }
618 
619     @Override
onViewAttachedToWindow(Presenter.ViewHolder holder)620     public final void onViewAttachedToWindow(Presenter.ViewHolder holder) {
621         onRowViewAttachedToWindow(getRowViewHolder(holder));
622     }
623 
624     /**
625      * Invoked when the row view is attached to the window.
626      */
onRowViewAttachedToWindow(ViewHolder vh)627     protected void onRowViewAttachedToWindow(ViewHolder vh) {
628         if (vh.mHeaderViewHolder != null) {
629             mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder);
630         }
631     }
632 
633     @Override
onViewDetachedFromWindow(Presenter.ViewHolder holder)634     public final void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
635         onRowViewDetachedFromWindow(getRowViewHolder(holder));
636     }
637 
638     /**
639      * Invoked when the row view is detached from the window.
640      */
onRowViewDetachedFromWindow(ViewHolder vh)641     protected void onRowViewDetachedFromWindow(ViewHolder vh) {
642         if (vh.mHeaderViewHolder != null) {
643             mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder);
644         }
645         cancelAnimationsRecursive(vh.view);
646     }
647 
648     /**
649      * Freezes/unfreezes the row, typically used when a transition starts/ends.
650      * This method is called by the fragment, it should not call it directly by the application.
651      */
freeze(ViewHolder holder, boolean freeze)652     public void freeze(ViewHolder holder, boolean freeze) {
653     }
654 
655     /**
656      * Changes the visibility of views.  The entrance transition will be run against the views that
657      * change visibilities.  A subclass may override and begin with calling
658      * super.setEntranceTransitionState().  This method is called by the fragment,
659      * it should not be called directly by the application.
660      *
661      * @param holder         The ViewHolder of the row.
662      * @param afterEntrance  true if children of row participating in entrance transition
663      *                       should be set to visible, false otherwise.
664      */
setEntranceTransitionState(ViewHolder holder, boolean afterEntrance)665     public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) {
666         if (holder.mHeaderViewHolder != null &&
667                 holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
668             holder.mHeaderViewHolder.view.setVisibility(afterEntrance ?
669                     View.VISIBLE : View.INVISIBLE);
670         }
671     }
672 }
673