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