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.app;
15 
16 import java.util.ArrayList;
17 
18 import android.animation.TimeAnimator;
19 import android.animation.TimeAnimator.TimeListener;
20 import android.os.Bundle;
21 import android.support.v17.leanback.R;
22 import android.support.v17.leanback.widget.ItemBridgeAdapter;
23 import android.support.v17.leanback.widget.OnItemViewClickedListener;
24 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
25 import android.support.v17.leanback.widget.RowPresenter.ViewHolder;
26 import android.support.v17.leanback.widget.ScaleFrameLayout;
27 import android.support.v17.leanback.widget.VerticalGridView;
28 import android.support.v17.leanback.widget.HorizontalGridView;
29 import android.support.v17.leanback.widget.OnItemSelectedListener;
30 import android.support.v17.leanback.widget.OnItemClickedListener;
31 import android.support.v17.leanback.widget.RowPresenter;
32 import android.support.v17.leanback.widget.ListRowPresenter;
33 import android.support.v17.leanback.widget.Presenter;
34 import android.support.v7.widget.RecyclerView;
35 import android.util.Log;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.ViewTreeObserver;
40 import android.view.animation.DecelerateInterpolator;
41 import android.view.animation.Interpolator;
42 
43 /**
44  * An ordered set of rows of leanback widgets.
45  */
46 public class RowsFragment extends BaseRowFragment {
47 
48     /**
49      * Internal helper class that manages row select animation and apply a default
50      * dim to each row.
51      */
52     final class RowViewHolderExtra implements TimeListener {
53         final RowPresenter mRowPresenter;
54         final Presenter.ViewHolder mRowViewHolder;
55 
56         final TimeAnimator mSelectAnimator = new TimeAnimator();
57 
58         int mSelectAnimatorDurationInUse;
59         Interpolator mSelectAnimatorInterpolatorInUse;
60         float mSelectLevelAnimStart;
61         float mSelectLevelAnimDelta;
62 
RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh)63         RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh) {
64             mRowPresenter = (RowPresenter) ibvh.getPresenter();
65             mRowViewHolder = ibvh.getViewHolder();
66             mSelectAnimator.setTimeListener(this);
67         }
68 
69         @Override
onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime)70         public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
71             if (mSelectAnimator.isRunning()) {
72                 updateSelect(totalTime, deltaTime);
73             }
74         }
75 
updateSelect(long totalTime, long deltaTime)76         void updateSelect(long totalTime, long deltaTime) {
77             float fraction;
78             if (totalTime >= mSelectAnimatorDurationInUse) {
79                 fraction = 1;
80                 mSelectAnimator.end();
81             } else {
82                 fraction = (float) (totalTime / (double) mSelectAnimatorDurationInUse);
83             }
84             if (mSelectAnimatorInterpolatorInUse != null) {
85                 fraction = mSelectAnimatorInterpolatorInUse.getInterpolation(fraction);
86             }
87             float level =  mSelectLevelAnimStart + fraction * mSelectLevelAnimDelta;
88             mRowPresenter.setSelectLevel(mRowViewHolder, level);
89         }
90 
animateSelect(boolean select, boolean immediate)91         void animateSelect(boolean select, boolean immediate) {
92             endSelectAnimation();
93             final float end = select ? 1 : 0;
94             if (immediate) {
95                 mRowPresenter.setSelectLevel(mRowViewHolder, end);
96             } else if (mRowPresenter.getSelectLevel(mRowViewHolder) != end) {
97                 mSelectAnimatorDurationInUse = mSelectAnimatorDuration;
98                 mSelectAnimatorInterpolatorInUse = mSelectAnimatorInterpolator;
99                 mSelectLevelAnimStart = mRowPresenter.getSelectLevel(mRowViewHolder);
100                 mSelectLevelAnimDelta = end - mSelectLevelAnimStart;
101                 mSelectAnimator.start();
102             }
103         }
104 
endAnimations()105         void endAnimations() {
106             endSelectAnimation();
107         }
108 
endSelectAnimation()109         void endSelectAnimation() {
110             mSelectAnimator.end();
111         }
112 
113     }
114 
115     private static final String TAG = "RowsFragment";
116     private static final boolean DEBUG = false;
117 
118     private ItemBridgeAdapter.ViewHolder mSelectedViewHolder;
119     private boolean mExpand = true;
120     private boolean mViewsCreated;
121     private float mRowScaleFactor;
122     private int mAlignedTop;
123     private boolean mRowScaleEnabled;
124     private ScaleFrameLayout mScaleFrameLayout;
125     private boolean mInTransition;
126     private boolean mAfterEntranceTransition = true;
127 
128     private OnItemSelectedListener mOnItemSelectedListener;
129     private OnItemViewSelectedListener mOnItemViewSelectedListener;
130     private OnItemClickedListener mOnItemClickedListener;
131     private OnItemViewClickedListener mOnItemViewClickedListener;
132 
133     // Select animation and interpolator are not intended to be
134     // exposed at this moment. They might be synced with vertical scroll
135     // animation later.
136     int mSelectAnimatorDuration;
137     Interpolator mSelectAnimatorInterpolator = new DecelerateInterpolator(2);
138 
139     private RecyclerView.RecycledViewPool mRecycledViewPool;
140     private ArrayList<Presenter> mPresenterMapper;
141 
142     private ItemBridgeAdapter.AdapterListener mExternalAdapterListener;
143 
144     @Override
findGridViewFromRoot(View view)145     protected VerticalGridView findGridViewFromRoot(View view) {
146         return (VerticalGridView) view.findViewById(R.id.container_list);
147     }
148 
149     /**
150      * Sets an item clicked listener on the fragment.
151      * OnItemClickedListener will override {@link View.OnClickListener} that
152      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
153      * So in general,  developer should choose one of the listeners but not both.
154      * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
155      */
setOnItemClickedListener(OnItemClickedListener listener)156     public void setOnItemClickedListener(OnItemClickedListener listener) {
157         mOnItemClickedListener = listener;
158         if (mViewsCreated) {
159             throw new IllegalStateException(
160                     "Item clicked listener must be set before views are created");
161         }
162     }
163 
164     /**
165      * Returns the item clicked listener.
166      * @deprecated Use {@link #getOnItemClickedListener()}
167      */
getOnItemClickedListener()168     public OnItemClickedListener getOnItemClickedListener() {
169         return mOnItemClickedListener;
170     }
171 
172     /**
173      * Sets an item clicked listener on the fragment.
174      * OnItemViewClickedListener will override {@link View.OnClickListener} that
175      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
176      * So in general,  developer should choose one of the listeners but not both.
177      */
setOnItemViewClickedListener(OnItemViewClickedListener listener)178     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
179         mOnItemViewClickedListener = listener;
180         if (mViewsCreated) {
181             throw new IllegalStateException(
182                     "Item clicked listener must be set before views are created");
183         }
184     }
185 
186     /**
187      * Returns the item clicked listener.
188      */
getOnItemViewClickedListener()189     public OnItemViewClickedListener getOnItemViewClickedListener() {
190         return mOnItemViewClickedListener;
191     }
192 
193     /**
194      * Set the visibility of titles/hovercard of browse rows.
195      */
setExpand(boolean expand)196     public void setExpand(boolean expand) {
197         mExpand = expand;
198         VerticalGridView listView = getVerticalGridView();
199         if (listView != null) {
200             updateRowScaling();
201             final int count = listView.getChildCount();
202             if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
203             for (int i = 0; i < count; i++) {
204                 View view = listView.getChildAt(i);
205                 ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
206                 setRowViewExpanded(vh, mExpand);
207             }
208         }
209     }
210 
211     /**
212      * Sets an item selection listener.
213      * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
214      */
setOnItemSelectedListener(OnItemSelectedListener listener)215     public void setOnItemSelectedListener(OnItemSelectedListener listener) {
216         mOnItemSelectedListener = listener;
217         VerticalGridView listView = getVerticalGridView();
218         if (listView != null) {
219             final int count = listView.getChildCount();
220             for (int i = 0; i < count; i++) {
221                 View view = listView.getChildAt(i);
222                 ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
223                         listView.getChildViewHolder(view);
224                 setOnItemSelectedListener(vh, mOnItemSelectedListener);
225             }
226         }
227     }
228 
229     /**
230      * Sets an item selection listener.
231      */
setOnItemViewSelectedListener(OnItemViewSelectedListener listener)232     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
233         mOnItemViewSelectedListener = listener;
234         VerticalGridView listView = getVerticalGridView();
235         if (listView != null) {
236             final int count = listView.getChildCount();
237             for (int i = 0; i < count; i++) {
238                 View view = listView.getChildAt(i);
239                 ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
240                         listView.getChildViewHolder(view);
241                 setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
242             }
243         }
244     }
245 
246     /**
247      * Returns an item selection listener.
248      */
getOnItemViewSelectedListener()249     public OnItemViewSelectedListener getOnItemViewSelectedListener() {
250         return mOnItemViewSelectedListener;
251     }
252 
253     /**
254      * Enables scaling of rows.
255      *
256      * @param enable true to enable row scaling
257      */
enableRowScaling(boolean enable)258     public void enableRowScaling(boolean enable) {
259         mRowScaleEnabled = enable;
260     }
261 
262     @Override
onRowSelected(ViewGroup parent, View view, int position, long id)263     void onRowSelected(ViewGroup parent, View view, int position, long id) {
264         VerticalGridView listView = getVerticalGridView();
265         if (listView == null) {
266             return;
267         }
268         ItemBridgeAdapter.ViewHolder vh = (view == null) ? null :
269             (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
270 
271         if (mSelectedViewHolder != vh) {
272             if (DEBUG) Log.v(TAG, "new row selected position " + position + " view " + view);
273 
274             if (mSelectedViewHolder != null) {
275                 setRowViewSelected(mSelectedViewHolder, false, false);
276             }
277             mSelectedViewHolder = vh;
278             if (mSelectedViewHolder != null) {
279                 setRowViewSelected(mSelectedViewHolder, true, false);
280             }
281         }
282     }
283 
284     @Override
getLayoutResourceId()285     int getLayoutResourceId() {
286         return R.layout.lb_rows_fragment;
287     }
288 
289     @Override
onCreate(Bundle savedInstanceState)290     public void onCreate(Bundle savedInstanceState) {
291         super.onCreate(savedInstanceState);
292         mSelectAnimatorDuration = getResources().getInteger(
293                 R.integer.lb_browse_rows_anim_duration);
294         mRowScaleFactor = getResources().getFraction(
295                 R.fraction.lb_browse_rows_scale, 1, 1);
296     }
297 
298     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)299     public View onCreateView(LayoutInflater inflater, ViewGroup container,
300             Bundle savedInstanceState) {
301         View view = super.onCreateView(inflater, container, savedInstanceState);
302         mScaleFrameLayout = (ScaleFrameLayout) view.findViewById(R.id.scale_frame);
303         return view;
304     }
305 
306     @Override
onViewCreated(View view, Bundle savedInstanceState)307     public void onViewCreated(View view, Bundle savedInstanceState) {
308         if (DEBUG) Log.v(TAG, "onViewCreated");
309         super.onViewCreated(view, savedInstanceState);
310         // Align the top edge of child with id row_content.
311         // Need set this for directly using RowsFragment.
312         getVerticalGridView().setItemAlignmentViewId(R.id.row_content);
313         getVerticalGridView().setSaveChildrenPolicy(VerticalGridView.SAVE_LIMITED_CHILD);
314 
315         mRecycledViewPool = null;
316         mPresenterMapper = null;
317     }
318 
319     @Override
onDestroyView()320     public void onDestroyView() {
321         mViewsCreated = false;
322         super.onDestroyView();
323     }
324 
325     @Override
setItemAlignment()326     void setItemAlignment() {
327         super.setItemAlignment();
328         if (getVerticalGridView() != null) {
329             getVerticalGridView().setItemAlignmentOffsetWithPadding(true);
330         }
331     }
332 
setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener)333     void setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener) {
334         mExternalAdapterListener = listener;
335     }
336 
337     /**
338      * Get the view that will change scale.
339      */
getScaleView()340     View getScaleView() {
341         return getVerticalGridView();
342     }
343 
344     /**
345      * Set pivots to scale rows fragment.
346      */
setScalePivots(float pivotX, float pivotY)347     void setScalePivots(float pivotX, float pivotY) {
348         // set pivot on ScaleFrameLayout, it will be propagated to its child VerticalGridView
349         // where we actually change scale.
350         mScaleFrameLayout.setPivotX(pivotX);
351         mScaleFrameLayout.setPivotY(pivotY);
352     }
353 
setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded)354     private static void setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded) {
355         ((RowPresenter) vh.getPresenter()).setRowViewExpanded(vh.getViewHolder(), expanded);
356     }
357 
setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected, boolean immediate)358     private static void setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected,
359             boolean immediate) {
360         RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
361         extra.animateSelect(selected, immediate);
362         ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected);
363     }
364 
setOnItemSelectedListener(ItemBridgeAdapter.ViewHolder vh, OnItemSelectedListener listener)365     private static void setOnItemSelectedListener(ItemBridgeAdapter.ViewHolder vh,
366             OnItemSelectedListener listener) {
367         ((RowPresenter) vh.getPresenter()).setOnItemSelectedListener(listener);
368     }
369 
setOnItemViewSelectedListener(ItemBridgeAdapter.ViewHolder vh, OnItemViewSelectedListener listener)370     private static void setOnItemViewSelectedListener(ItemBridgeAdapter.ViewHolder vh,
371             OnItemViewSelectedListener listener) {
372         ((RowPresenter) vh.getPresenter()).setOnItemViewSelectedListener(listener);
373     }
374 
375     private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener =
376             new ItemBridgeAdapter.AdapterListener() {
377         @Override
378         public void onAddPresenter(Presenter presenter, int type) {
379             ((RowPresenter) presenter).setOnItemClickedListener(mOnItemClickedListener);
380             ((RowPresenter) presenter).setOnItemViewClickedListener(mOnItemViewClickedListener);
381             if (mExternalAdapterListener != null) {
382                 mExternalAdapterListener.onAddPresenter(presenter, type);
383             }
384         }
385         @Override
386         public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
387             VerticalGridView listView = getVerticalGridView();
388             if (listView != null) {
389                 // set clip children false for slide animation
390                 listView.setClipChildren(false);
391             }
392             setupSharedViewPool(vh);
393             mViewsCreated = true;
394             vh.setExtraObject(new RowViewHolderExtra(vh));
395             // selected state is initialized to false, then driven by grid view onChildSelected
396             // events.  When there is rebind, grid view fires onChildSelected event properly.
397             // So we don't need do anything special later in onBind or onAttachedToWindow.
398             setRowViewSelected(vh, false, true);
399             if (mExternalAdapterListener != null) {
400                 mExternalAdapterListener.onCreate(vh);
401             }
402         }
403         @Override
404         public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
405             if (DEBUG) Log.v(TAG, "onAttachToWindow");
406             // All views share the same mExpand value.  When we attach a view to grid view,
407             // we should make sure it pick up the latest mExpand value we set early on other
408             // attached views.  For no-structure-change update,  the view is rebound to new data,
409             // but again it should use the unchanged mExpand value,  so we don't need do any
410             // thing in onBind.
411             setRowViewExpanded(vh, mExpand);
412             setOnItemSelectedListener(vh, mOnItemSelectedListener);
413             setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
414             RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
415             RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
416             rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition);
417             if (mExternalAdapterListener != null) {
418                 mExternalAdapterListener.onAttachedToWindow(vh);
419             }
420         }
421         @Override
422         public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
423             if (mSelectedViewHolder == vh) {
424                 setRowViewSelected(mSelectedViewHolder, false, true);
425                 mSelectedViewHolder = null;
426             }
427             if (mExternalAdapterListener != null) {
428                 mExternalAdapterListener.onDetachedFromWindow(vh);
429             }
430         }
431         @Override
432         public void onBind(ItemBridgeAdapter.ViewHolder vh) {
433             if (mExternalAdapterListener != null) {
434                 mExternalAdapterListener.onBind(vh);
435             }
436         }
437         @Override
438         public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
439             RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
440             extra.endAnimations();
441             if (mExternalAdapterListener != null) {
442                 mExternalAdapterListener.onUnbind(vh);
443             }
444         }
445     };
446 
setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh)447     private void setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh) {
448         RowPresenter rowPresenter = (RowPresenter) bridgeVh.getPresenter();
449         RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(bridgeVh.getViewHolder());
450 
451         if (rowVh instanceof ListRowPresenter.ViewHolder) {
452             HorizontalGridView view = ((ListRowPresenter.ViewHolder) rowVh).getGridView();
453             // Recycled view pool is shared between all list rows
454             if (mRecycledViewPool == null) {
455                 mRecycledViewPool = view.getRecycledViewPool();
456             } else {
457                 view.setRecycledViewPool(mRecycledViewPool);
458             }
459 
460             ItemBridgeAdapter bridgeAdapter =
461                     ((ListRowPresenter.ViewHolder) rowVh).getBridgeAdapter();
462             if (mPresenterMapper == null) {
463                 mPresenterMapper = bridgeAdapter.getPresenterMapper();
464             } else {
465                 bridgeAdapter.setPresenterMapper(mPresenterMapper);
466             }
467         }
468     }
469 
470     @Override
updateAdapter()471     void updateAdapter() {
472         super.updateAdapter();
473         mSelectedViewHolder = null;
474         mViewsCreated = false;
475 
476         ItemBridgeAdapter adapter = getBridgeAdapter();
477         if (adapter != null) {
478             adapter.setAdapterListener(mBridgeAdapterListener);
479         }
480     }
481 
482     @Override
onTransitionStart()483     void onTransitionStart() {
484         super.onTransitionStart();
485         mInTransition = true;
486         freezeRows(true);
487     }
488 
489     class ExpandPreLayout implements ViewTreeObserver.OnPreDrawListener {
490 
491         final View mVerticalView;
492         final Runnable mCallback;
493         int mState;
494 
495         final static int STATE_INIT = 0;
496         final static int STATE_FIRST_DRAW = 1;
497         final static int STATE_SECOND_DRAW = 2;
498 
ExpandPreLayout(Runnable callback)499         ExpandPreLayout(Runnable callback) {
500             mVerticalView = getVerticalGridView();
501             mCallback = callback;
502         }
503 
execute()504         void execute() {
505             mVerticalView.getViewTreeObserver().addOnPreDrawListener(this);
506             setExpand(false);
507             mState = STATE_INIT;
508         }
509 
510         @Override
onPreDraw()511         public boolean onPreDraw() {
512             if (mState == STATE_INIT) {
513                 setExpand(true);
514                 mState = STATE_FIRST_DRAW;
515             } else if (mState == STATE_FIRST_DRAW) {
516                 mCallback.run();
517                 mVerticalView.getViewTreeObserver().removeOnPreDrawListener(this);
518                 mState = STATE_SECOND_DRAW;
519             }
520             return false;
521         }
522     }
523 
onExpandTransitionStart(boolean expand, final Runnable callback)524     void onExpandTransitionStart(boolean expand, final Runnable callback) {
525         onTransitionStart();
526         if (expand) {
527             callback.run();
528             return;
529         }
530         // Run a "pre" layout when we go non-expand, in order to get the initial
531         // positions of added rows.
532         new ExpandPreLayout(callback).execute();
533     }
534 
needsScale()535     private boolean needsScale() {
536         return mRowScaleEnabled && !mExpand;
537     }
538 
updateRowScaling()539     private void updateRowScaling() {
540         final float scaleFactor = needsScale() ? mRowScaleFactor : 1f;
541         mScaleFrameLayout.setLayoutScaleY(scaleFactor);
542         getScaleView().setScaleY(scaleFactor);
543         getScaleView().setScaleX(scaleFactor);
544         updateWindowAlignOffset();
545     }
546 
updateWindowAlignOffset()547     private void updateWindowAlignOffset() {
548         int alignOffset = mAlignedTop;
549         if (needsScale()) {
550             alignOffset = (int) (alignOffset / mRowScaleFactor + 0.5f);
551         }
552         getVerticalGridView().setWindowAlignmentOffset(alignOffset);
553     }
554 
555     @Override
setWindowAlignmentFromTop(int alignedTop)556     void setWindowAlignmentFromTop(int alignedTop) {
557         mAlignedTop = alignedTop;
558         final VerticalGridView gridView = getVerticalGridView();
559         if (gridView != null) {
560             updateWindowAlignOffset();
561             // align to a fixed position from top
562             gridView.setWindowAlignmentOffsetPercent(
563                     VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
564             gridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
565         }
566     }
567 
568     @Override
onTransitionEnd()569     void onTransitionEnd() {
570         super.onTransitionEnd();
571         mInTransition = false;
572         freezeRows(false);
573     }
574 
freezeRows(boolean freeze)575     private void freezeRows(boolean freeze) {
576         VerticalGridView verticalView = getVerticalGridView();
577         if (verticalView != null) {
578             final int count = verticalView.getChildCount();
579             for (int i = 0; i < count; i++) {
580                 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
581                     verticalView.getChildViewHolder(verticalView.getChildAt(i));
582                 RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
583                 RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
584                 rowPresenter.freeze(vh, freeze);
585             }
586         }
587     }
588 
589     /**
590      * For rows that willing to participate entrance transition,  this function
591      * hide views if afterTransition is true,  show views if afterTransition is false.
592      */
setEntranceTransitionState(boolean afterTransition)593     void setEntranceTransitionState(boolean afterTransition) {
594         mAfterEntranceTransition = afterTransition;
595         VerticalGridView verticalView = getVerticalGridView();
596         if (verticalView != null) {
597             final int count = verticalView.getChildCount();
598             for (int i = 0; i < count; i++) {
599                 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
600                     verticalView.getChildViewHolder(verticalView.getChildAt(i));
601                 RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
602                 RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
603                 rowPresenter.setEntranceTransitionState(vh, mAfterEntranceTransition);
604             }
605         }
606     }
607 }
608