RowPresenter.java revision 60bb6af2e336072921f5d3c3861e86b3cc6241b3
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 OnItemSelectedListener} and {@link OnItemClickedListener}.
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 view holder 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
154        /**
155         * Constructor for ViewHolder.
156         *
157         * @param view The View bound to the Row.
158         */
159        public ViewHolder(View view) {
160            super(view);
161            mColorDimmer = ColorOverlayDimmer.createDefault(view.getContext());
162        }
163
164        /**
165         * Returns the Row bound to the View in this ViewHolder.
166         */
167        public final Row getRow() {
168            return mRow;
169        }
170
171        /**
172         * Returns whether the Row is in its expanded state.
173         *
174         * @return true if the Row is expanded, false otherwise.
175         */
176        public final boolean isExpanded() {
177            return mExpanded;
178        }
179
180        /**
181         * Returns whether the Row is selected.
182         *
183         * @return true if the Row is selected, false otherwise.
184         */
185        public final boolean isSelected() {
186            return mSelected;
187        }
188
189        /**
190         * Returns the current selection level of the Row.
191         */
192        public final float getSelectLevel() {
193            return mSelectLevel;
194        }
195
196        /**
197         * Returns the view holder for the Row header for this Row.
198         */
199        public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() {
200            return mHeaderViewHolder;
201        }
202
203        /**
204         * Sets the row view's activated status.  The status will be applied to children through
205         * {@link #syncActivatedStatus(View)}.  Application should only call this function
206         * when {@link RowPresenter#getSyncActivatePolicy()} is
207         * {@link RowPresenter#SYNC_ACTIVATED_CUSTOM}; otherwise the value will
208         * be overwritten when expanded or selected status changes.
209         */
210        public final void setActivated(boolean activated) {
211            mActivated = activated ? ACTIVATED : NOT_ACTIVATED;
212        }
213
214        /**
215         * Synchronizes the activated status of view to the last value passed through
216         * {@link RowPresenter.ViewHolder#setActivated(boolean)}. No operation if
217         * {@link RowPresenter.ViewHolder#setActivated(boolean)} is never called.  Normally
218         * application does not need to call this method,  {@link ListRowPresenter} automatically
219         * calls this method when a child is attached to list row.   However if
220         * application writes its own custom RowPresenter, it should call this method
221         * when attaches a child to the row view.
222         */
223        public final void syncActivatedStatus(View view) {
224            if (mActivated == ACTIVATED) {
225                view.setActivated(true);
226            } else if (mActivated == NOT_ACTIVATED) {
227                view.setActivated(false);
228            }
229        }
230
231        /**
232         * Sets a key listener.
233         */
234        public void setOnKeyListener(View.OnKeyListener keyListener) {
235            mOnKeyListener = keyListener;
236        }
237
238        /**
239         * Returns the key listener.
240         */
241        public View.OnKeyListener getOnKeyListener() {
242            return mOnKeyListener;
243        }
244    }
245
246    private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
247    private OnItemSelectedListener mOnItemSelectedListener;
248    private OnItemClickedListener mOnItemClickedListener;
249    private OnItemViewSelectedListener mOnItemViewSelectedListener;
250    private OnItemViewClickedListener mOnItemViewClickedListener;
251
252    boolean mSelectEffectEnabled = true;
253    int mSyncActivatePolicy = SYNC_ACTIVATED_TO_EXPANDED;
254
255
256    /**
257     * Constructs a RowPresenter.
258     */
259    public RowPresenter() {
260        mHeaderPresenter.setNullItemVisibilityGone(true);
261    }
262
263    @Override
264    public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
265        ViewHolder vh = createRowViewHolder(parent);
266        vh.mInitialzed = false;
267        Presenter.ViewHolder result;
268        if (needsRowContainerView()) {
269            RowContainerView containerView = new RowContainerView(parent.getContext());
270            if (mHeaderPresenter != null) {
271                vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder)
272                        mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view);
273            }
274            result = new ContainerViewHolder(containerView, vh);
275        } else {
276            result = vh;
277        }
278        initializeRowViewHolder(vh);
279        if (!vh.mInitialzed) {
280            throw new RuntimeException("super.initializeRowViewHolder() must be called");
281        }
282        return result;
283    }
284
285    /**
286     * Called to create a ViewHolder object for a Row. Subclasses will override
287     * this method to return a different concrete ViewHolder object.
288     *
289     * @param parent The parent View for the Row's view holder.
290     * @return A ViewHolder for the Row's View.
291     */
292    protected abstract ViewHolder createRowViewHolder(ViewGroup parent);
293
294    /**
295     * Called after a {@link RowPresenter.ViewHolder} is created for a Row.
296     * Subclasses may override this method and start by calling
297     * super.initializeRowViewHolder(ViewHolder).
298     *
299     * @param vh The ViewHolder to initialize for the Row.
300     */
301    protected void initializeRowViewHolder(ViewHolder vh) {
302        vh.mInitialzed = true;
303        // set clip children to false for slide transition
304        if (vh.view instanceof ViewGroup) {
305            ((ViewGroup) vh.view).setClipChildren(false);
306        }
307        if (vh.mContainerViewHolder != null) {
308            ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false);
309        }
310    }
311
312    /**
313     * Set the Presenter used for rendering the header. Can be null to disable
314     * header rendering. The method must be called before creating any Row Views.
315     */
316    public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) {
317        mHeaderPresenter = headerPresenter;
318    }
319
320    /**
321     * Get the Presenter used for rendering the header, or null if none has been
322     * set.
323     */
324    public final RowHeaderPresenter getHeaderPresenter() {
325        return mHeaderPresenter;
326    }
327
328    /**
329     * Get the {@link RowPresenter.ViewHolder} from the given Presenter
330     * ViewHolder.
331     */
332    public final ViewHolder getRowViewHolder(Presenter.ViewHolder holder) {
333        if (holder instanceof ContainerViewHolder) {
334            return ((ContainerViewHolder) holder).mRowViewHolder;
335        } else {
336            return (ViewHolder) holder;
337        }
338    }
339
340    /**
341     * Set the expanded state of a Row view.
342     *
343     * @param holder The Row ViewHolder to set expanded state on.
344     * @param expanded True if the Row is expanded, false otherwise.
345     */
346    public final void setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded) {
347        ViewHolder rowViewHolder = getRowViewHolder(holder);
348        rowViewHolder.mExpanded = expanded;
349        onRowViewExpanded(rowViewHolder, expanded);
350    }
351
352    /**
353     * Set the selected state of a Row view.
354     *
355     * @param holder The Row ViewHolder to set expanded state on.
356     * @param selected True if the Row is expanded, false otherwise.
357     */
358    public final void setRowViewSelected(Presenter.ViewHolder holder, boolean selected) {
359        ViewHolder rowViewHolder = getRowViewHolder(holder);
360        rowViewHolder.mSelected = selected;
361        onRowViewSelected(rowViewHolder, selected);
362    }
363
364    /**
365     * Subclass may override this to respond to expanded state changes of a Row.
366     * The default implementation will hide/show the header view. Subclasses may
367     * make visual changes to the Row View but must not create animation on the
368     * Row view.
369     */
370    protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
371        updateHeaderViewVisibility(vh);
372        updateActivateStatus(vh, vh.view);
373    }
374
375    /**
376     * Update view's activate status according to {@link #getSyncActivatePolicy()} and the
377     * selected status and expanded status of the RowPresenter ViewHolder.
378     */
379    private void updateActivateStatus(ViewHolder vh, View view) {
380        switch (mSyncActivatePolicy) {
381            case SYNC_ACTIVATED_TO_EXPANDED:
382                vh.setActivated(vh.isExpanded());
383                break;
384            case SYNC_ACTIVATED_TO_SELECTED:
385                vh.setActivated(vh.isSelected());
386                break;
387            case SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED:
388                vh.setActivated(vh.isExpanded() && vh.isSelected());
389                break;
390        }
391        vh.syncActivatedStatus(view);
392    }
393
394    /**
395     * Sets policy of updating row view activated status.  Can be one of:
396     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
397     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
398     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
399     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
400     */
401    public final void setSyncActivatePolicy(int syncActivatePolicy) {
402        mSyncActivatePolicy = syncActivatePolicy;
403    }
404
405    /**
406     * Returns policy of updating row view activated status.  Can be one of:
407     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
408     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
409     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
410     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
411     */
412    public final int getSyncActivatePolicy() {
413        return mSyncActivatePolicy;
414    }
415
416    /**
417     * Subclass may override this to respond to selected state changes of a Row.
418     * Subclass may make visual changes to Row view but must not create
419     * animation on the Row view.
420     */
421    protected void onRowViewSelected(ViewHolder vh, boolean selected) {
422        if (selected) {
423            if (mOnItemViewSelectedListener != null) {
424                mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRow());
425            }
426            if (mOnItemSelectedListener != null) {
427                mOnItemSelectedListener.onItemSelected(null, vh.getRow());
428            }
429        }
430        updateHeaderViewVisibility(vh);
431        updateActivateStatus(vh, vh.view);
432    }
433
434    private void updateHeaderViewVisibility(ViewHolder vh) {
435        if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
436            RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view);
437            containerView.showHeader(vh.isExpanded());
438        }
439    }
440
441    /**
442     * Set the current select level to a value between 0 (unselected) and 1 (selected).
443     * Subclasses may override {@link #onSelectLevelChanged(ViewHolder)} to
444     * respond to changes in the selected level.
445     */
446    public final void setSelectLevel(Presenter.ViewHolder vh, float level) {
447        ViewHolder rowViewHolder = getRowViewHolder(vh);
448        rowViewHolder.mSelectLevel = level;
449        onSelectLevelChanged(rowViewHolder);
450    }
451
452    /**
453     * Get the current select level. The value will be between 0 (unselected)
454     * and 1 (selected).
455     */
456    public final float getSelectLevel(Presenter.ViewHolder vh) {
457        return getRowViewHolder(vh).mSelectLevel;
458    }
459
460    /**
461     * Callback when select level is changed. The default implementation applies
462     * the select level to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)}
463     * when {@link #getSelectEffectEnabled()} is true. Subclasses may override
464     * this function and implement a different select effect. In this case, you
465     * should also override {@link #isUsingDefaultSelectEffect()} to disable
466     * the default dimming effect applied by the library.
467     */
468    protected void onSelectLevelChanged(ViewHolder vh) {
469        if (getSelectEffectEnabled()) {
470            vh.mColorDimmer.setActiveLevel(vh.mSelectLevel);
471            if (vh.mHeaderViewHolder != null) {
472                mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel);
473            }
474            if (isUsingDefaultSelectEffect()) {
475                ((RowContainerView) vh.mContainerViewHolder.view).setForegroundColor(
476                        vh.mColorDimmer.getPaint().getColor());
477            }
478        }
479    }
480
481    /**
482     * Enables or disables the row selection effect.
483     * This will not only affect the default dim effect, but subclasses must
484     * respect this flag as well.
485     */
486    public final void setSelectEffectEnabled(boolean applyDimOnSelect) {
487        mSelectEffectEnabled = applyDimOnSelect;
488    }
489
490    /**
491     * Returns true if the row selection effect is enabled.
492     * This value not only determines whether the default dim implementation is
493     * used, but subclasses must also respect this flag.
494     */
495    public final boolean getSelectEffectEnabled() {
496        return mSelectEffectEnabled;
497    }
498
499    /**
500     * Return whether this RowPresenter is using the default dimming effect
501     * provided by the library.  Subclasses may(most likely) return false and
502     * override {@link #onSelectLevelChanged(ViewHolder)}.
503     */
504    public boolean isUsingDefaultSelectEffect() {
505        return true;
506    }
507
508    final boolean needsDefaultSelectEffect() {
509        return isUsingDefaultSelectEffect() && getSelectEffectEnabled();
510    }
511
512    final boolean needsRowContainerView() {
513        return mHeaderPresenter != null || needsDefaultSelectEffect();
514    }
515
516    /**
517     * Return true if the Row view can draw outside its bounds.
518     */
519    public boolean canDrawOutOfBounds() {
520        return false;
521    }
522
523    @Override
524    public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
525        onBindRowViewHolder(getRowViewHolder(viewHolder), item);
526    }
527
528    protected void onBindRowViewHolder(ViewHolder vh, Object item) {
529        vh.mRow = (Row) item;
530        if (vh.mHeaderViewHolder != null) {
531            mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item);
532        }
533    }
534
535    @Override
536    public final void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
537        onUnbindRowViewHolder(getRowViewHolder(viewHolder));
538    }
539
540    protected void onUnbindRowViewHolder(ViewHolder vh) {
541        if (vh.mHeaderViewHolder != null) {
542            mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder);
543        }
544        vh.mRow = null;
545    }
546
547    @Override
548    public final void onViewAttachedToWindow(Presenter.ViewHolder holder) {
549        onRowViewAttachedToWindow(getRowViewHolder(holder));
550    }
551
552    protected void onRowViewAttachedToWindow(ViewHolder vh) {
553        if (vh.mHeaderViewHolder != null) {
554            mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder);
555        }
556    }
557
558    @Override
559    public final void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
560        onRowViewDetachedFromWindow(getRowViewHolder(holder));
561    }
562
563    protected void onRowViewDetachedFromWindow(ViewHolder vh) {
564        if (vh.mHeaderViewHolder != null) {
565            mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder);
566        }
567        cancelAnimationsRecursive(vh.view);
568    }
569
570    /**
571     * Set the listener for item or row selection. A RowPresenter fires a row
572     * selection event with a null item. Subclasses (e.g. {@link ListRowPresenter})
573     * can fire a selection event with the selected item.
574     */
575    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
576        mOnItemSelectedListener = listener;
577    }
578
579    /**
580     * Get the listener for item or row selection.
581     */
582    public final OnItemSelectedListener getOnItemSelectedListener() {
583        return mOnItemSelectedListener;
584    }
585
586    /**
587     * Set the listener for item click events. A RowPresenter does not use this
588     * listener, but a subclass may fire an item click event if it has the concept
589     * of an item. The {@link OnItemClickedListener} will override any
590     * {@link View.OnClickListener} that an item's Presenter sets during
591     * {@link Presenter#onCreateViewHolder(ViewGroup)}. So in general, you
592     * should choose to use an OnItemClickedListener or a {@link
593     * View.OnClickListener}, but not both.
594     */
595    public final void setOnItemClickedListener(OnItemClickedListener listener) {
596        mOnItemClickedListener = listener;
597    }
598
599    /**
600     * Get the listener for item click events.
601     */
602    public final OnItemClickedListener getOnItemClickedListener() {
603        return mOnItemClickedListener;
604    }
605
606    /**
607     * Set listener for item or row selection.  RowPresenter fires row selection
608     * event with null item, subclass of RowPresenter e.g. {@link ListRowPresenter} can
609     * fire a selection event with selected item.
610     */
611    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
612        mOnItemViewSelectedListener = listener;
613    }
614
615    /**
616     * Get listener for item or row selection.
617     */
618    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
619        return mOnItemViewSelectedListener;
620    }
621
622    /**
623     * Set listener for item click event.  RowPresenter does nothing but subclass of
624     * RowPresenter may fire item click event if it does have a concept of item.
625     * OnItemViewClickedListener will override {@link View.OnClickListener} that
626     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
627     * So in general,  developer should choose one of the listeners but not both.
628     */
629    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
630        mOnItemViewClickedListener = listener;
631    }
632
633    /**
634     * Set listener for item click event.
635     */
636    public final OnItemViewClickedListener getOnItemViewClickedListener() {
637        return mOnItemViewClickedListener;
638    }
639
640    /**
641     * Freeze/Unfreeze the row, typically used when transition starts/ends.
642     * This method is called by fragment, app should not call it directly.
643     */
644    public void freeze(ViewHolder holder, boolean freeze) {
645    }
646
647    /**
648     * Change visibility of views, entrance transition will be run against the views that
649     * change visibilities.  Subclass may override and begin with calling
650     * super.setEntranceTransitionState().  This method is called by fragment,
651     * app should not call it directly.
652     */
653    public void setEntranceTransitionState(ViewHolder holder, boolean afterTransition) {
654        if (holder.mHeaderViewHolder != null &&
655                holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
656            holder.mHeaderViewHolder.view.setVisibility(afterTransition ?
657                    View.VISIBLE : View.INVISIBLE);
658        }
659    }
660}
661