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