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