Home | History | Annotate | Download | only in widget
      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 
    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          */
    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          */
    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          */
    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          */
    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          */
    202         public final boolean isSelected() {
    203             return mSelected;
    204         }
    205 
    206         /**
    207          * Returns the current selection level of the Row.
    208          */
    209         public final float getSelectLevel() {
    210             return mSelectLevel;
    211         }
    212 
    213         /**
    214          * Returns the view holder for the Row header for this Row.
    215          */
    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          */
    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          */
    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          */
    251         public void setOnKeyListener(View.OnKeyListener keyListener) {
    252             mOnKeyListener = keyListener;
    253         }
    254 
    255         /**
    256          * Returns the key listener.
    257          */
    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          */
    267         public final void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
    268             mOnItemViewSelectedListener = listener;
    269         }
    270 
    271         /**
    272          * Returns the listener for item or row selection.
    273          */
    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          */
    284         public final void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
    285             mOnItemViewClickedListener = listener;
    286         }
    287 
    288         /**
    289          * Returns the listener for item click event.
    290          */
    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          */
    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          */
    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      */
    320     public RowPresenter() {
    321         mHeaderPresenter.setNullItemVisibilityGone(true);
    322     }
    323 
    324     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    397     public final RowHeaderPresenter getHeaderPresenter() {
    398         return mHeaderPresenter;
    399     }
    400 
    401     /**
    402      * Returns the {@link RowPresenter.ViewHolder} from the given RowPresenter
    403      * ViewHolder.
    404      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    510     protected void onRowViewSelected(ViewHolder vh, boolean selected) {
    511         dispatchItemSelectedListener(vh, selected);
    512         updateHeaderViewVisibility(vh);
    513         updateActivateStatus(vh, vh.view);
    514     }
    515 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    586     public boolean isUsingDefaultSelectEffect() {
    587         return true;
    588     }
    589 
    590     final boolean needsDefaultSelectEffect() {
    591         return isUsingDefaultSelectEffect() && getSelectEffectEnabled();
    592     }
    593 
    594     final boolean needsRowContainerView() {
    595         return mHeaderPresenter != null || needsDefaultSelectEffect();
    596     }
    597 
    598     @Override
    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      */
    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
    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      */
    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
    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      */
    643     protected void onRowViewAttachedToWindow(ViewHolder vh) {
    644         if (vh.mHeaderViewHolder != null) {
    645             mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder);
    646         }
    647     }
    648 
    649     @Override
    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      */
    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      */
    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      */
    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