ListRowPresenter.java revision 5870e4aca782a55d0e8f7650f06aec86080b2f88
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.content.Context;
17import android.content.res.TypedArray;
18import android.support.v17.leanback.R;
19import android.util.Log;
20import android.view.KeyEvent;
21import android.view.View;
22import android.view.ViewGroup;
23import android.view.ViewGroup.LayoutParams;
24
25import java.util.HashMap;
26
27/**
28 * ListRowPresenter renders {@link ListRow} using a
29 * {@link HorizontalGridView} hosted in a {@link ListRowView}.
30 *
31 * <h3>Hover card</h3>
32 * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to
33 * display a view for the currently focused list item below the rendered
34 * list. This view is known as a hover card.
35 *
36 * <h3>Selection animation</h3>
37 * ListRowPresenter disables {@link RowPresenter}'s default dimming effect and draw
38 * a dim overlay on top of each individual child items.  Subclass may override and disable
39 * {@link #isUsingDefaultListSelectEffect()} and write its own dim effect in
40 * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)}.
41 *
42 * <h3>Shadow</h3>
43 * ListRowPresenter applies a default shadow to child of each view.  Call
44 * {@link #setShadowEnabled(boolean)} to disable shadow.  Subclass may override and return
45 * false in {@link #isUsingDefaultShadow()} and replace with its own shadow implementation.
46 */
47public class ListRowPresenter extends RowPresenter {
48
49    private static final String TAG = "ListRowPresenter";
50    private static final boolean DEBUG = false;
51
52    private static final int DEFAULT_RECYCLED_POOL_SIZE = 24;
53
54    public static class ViewHolder extends RowPresenter.ViewHolder {
55        final ListRowPresenter mListRowPresenter;
56        final HorizontalGridView mGridView;
57        ItemBridgeAdapter mItemBridgeAdapter;
58        final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
59        final int mPaddingTop;
60        final int mPaddingBottom;
61        final int mPaddingLeft;
62        final int mPaddingRight;
63
64        public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) {
65            super(rootView);
66            mGridView = gridView;
67            mListRowPresenter = p;
68            mPaddingTop = mGridView.getPaddingTop();
69            mPaddingBottom = mGridView.getPaddingBottom();
70            mPaddingLeft = mGridView.getPaddingLeft();
71            mPaddingRight = mGridView.getPaddingRight();
72        }
73
74        public final ListRowPresenter getListRowPresenter() {
75            return mListRowPresenter;
76        }
77
78        public final HorizontalGridView getGridView() {
79            return mGridView;
80        }
81
82        public final ItemBridgeAdapter getBridgeAdapter() {
83            return mItemBridgeAdapter;
84        }
85    }
86
87    class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter {
88        ListRowPresenter.ViewHolder mRowViewHolder;
89
90        ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) {
91            mRowViewHolder = rowViewHolder;
92        }
93
94        @Override
95        public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
96            // Only when having an OnItemClickListner, we will attach the OnClickListener.
97            if (getOnItemViewClickedListener() != null) {
98                viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
99                    @Override
100                    public void onClick(View v) {
101                        ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
102                                mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView);
103                        if (getOnItemViewClickedListener() != null) {
104                            getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
105                                    ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow);
106                        }
107                    }
108                });
109            }
110        }
111
112        @Override
113        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
114            if (getOnItemViewClickedListener() != null) {
115                viewHolder.mHolder.view.setOnClickListener(null);
116            }
117        }
118
119        @Override
120        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
121            if (needsDefaultListSelectEffect()) {
122                int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor();
123                ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
124            }
125            mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
126        }
127
128        @Override
129        public void onAddPresenter(Presenter presenter, int type) {
130            mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
131                    type, getRecycledPoolSize(presenter));
132        }
133    }
134
135    private int mRowHeight;
136    private int mExpandedRowHeight;
137    private PresenterSelector mHoverCardPresenterSelector;
138    private int mFocusZoomFactor;
139    private boolean mUseFocusDimmer;
140    private boolean mShadowEnabled = true;
141    private int mBrowseRowsFadingEdgeLength = -1;
142    private boolean mRoundedCornersEnabled = true;
143    private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>();
144
145    private static int sSelectedRowTopPadding;
146    private static int sExpandedSelectedRowTopPadding;
147    private static int sExpandedRowNoHovercardBottomPadding;
148
149    /**
150     * Constructs a ListRowPresenter with defaults.
151     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
152     * disabled dimming on focus.
153     */
154    public ListRowPresenter() {
155        this(FocusHighlight.ZOOM_FACTOR_MEDIUM);
156    }
157
158    /**
159     * Constructs a ListRowPresenter with the given parameters.
160     *
161     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
162     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
163     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
164     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
165     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
166     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
167     * Dimming on focus defaults to disabled.
168     */
169    public ListRowPresenter(int focusZoomFactor) {
170        this(focusZoomFactor, false);
171    }
172
173    /**
174     * Constructs a ListRowPresenter with the given parameters.
175     *
176     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
177     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
178     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
179     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
180     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
181     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
182     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
183     */
184    public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) {
185        if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) {
186            throw new IllegalArgumentException("Unhandled zoom factor");
187        }
188        mFocusZoomFactor = focusZoomFactor;
189        mUseFocusDimmer = useFocusDimmer;
190    }
191
192    /**
193     * Sets the row height for rows created by this Presenter. Rows
194     * created before calling this method will not be updated.
195     *
196     * @param rowHeight Row height in pixels, or WRAP_CONTENT, or 0
197     * to use the default height.
198     */
199    public void setRowHeight(int rowHeight) {
200        mRowHeight = rowHeight;
201    }
202
203    /**
204     * Returns the row height for list rows created by this Presenter.
205     */
206    public int getRowHeight() {
207        return mRowHeight;
208    }
209
210    /**
211     * Sets the expanded row height for rows created by this Presenter.
212     * If not set, expanded rows have the same height as unexpanded
213     * rows.
214     *
215     * @param rowHeight The row height in to use when the row is expanded,
216     *        in pixels, or WRAP_CONTENT, or 0 to use the default.
217     */
218    public void setExpandedRowHeight(int rowHeight) {
219        mExpandedRowHeight = rowHeight;
220    }
221
222    /**
223     * Returns the expanded row height for rows created by this Presenter.
224     */
225    public int getExpandedRowHeight() {
226        return mExpandedRowHeight != 0 ? mExpandedRowHeight : mRowHeight;
227    }
228
229    /**
230     * Returns the zoom factor used for focus highlighting.
231     */
232    public final int getFocusZoomFactor() {
233        return mFocusZoomFactor;
234    }
235    /**
236     * Returns the zoom factor used for focus highlighting.
237     * @deprecated use {@link #getFocusZoomFactor} instead.
238     */
239    @Deprecated public final int getZoomFactor() {
240        return mFocusZoomFactor;
241    }
242
243    /**
244     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
245     */
246    public final boolean isFocusDimmerUsed() {
247        return mUseFocusDimmer;
248    }
249
250    private ItemBridgeAdapter.Wrapper mCardWrapper = new ItemBridgeAdapter.Wrapper() {
251        @Override
252        public View createWrapper(View root) {
253            ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext());
254            wrapper.setLayoutParams(
255                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
256            wrapper.initialize(needsDefaultShadow(),
257                    needsDefaultListSelectEffect(),
258                    areChildRoundedCornersEnabled());
259            return wrapper;
260        }
261        @Override
262        public void wrap(View wrapper, View wrapped) {
263            ((ShadowOverlayContainer) wrapper).wrap(wrapped);
264        }
265    };
266
267    @Override
268    protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
269        super.initializeRowViewHolder(holder);
270        final ViewHolder rowViewHolder = (ViewHolder) holder;
271        rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
272        if (needsDefaultListSelectEffect() || needsDefaultShadow()
273                || areChildRoundedCornersEnabled()) {
274            rowViewHolder.mItemBridgeAdapter.setWrapper(mCardWrapper);
275        }
276        if (needsDefaultShadow()) {
277            ShadowOverlayContainer.prepareParentForShadow(rowViewHolder.mGridView);
278        }
279        FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
280                mFocusZoomFactor, mUseFocusDimmer);
281        rowViewHolder.mGridView.setFocusDrawingOrderEnabled(!isUsingZOrder());
282        rowViewHolder.mGridView.setOnChildSelectedListener(
283                new OnChildSelectedListener() {
284            @Override
285            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
286                selectChildView(rowViewHolder, view);
287            }
288        });
289        rowViewHolder.mGridView.setOnUnhandledKeyListener(
290                new BaseGridView.OnUnhandledKeyListener() {
291            @Override
292            public boolean onUnhandledKey(KeyEvent event) {
293                if (rowViewHolder.getOnKeyListener() != null &&
294                        rowViewHolder.getOnKeyListener().onKey(
295                                rowViewHolder.view, event.getKeyCode(), event)) {
296                    return true;
297                }
298                return false;
299            }
300        });
301    }
302
303    final boolean needsDefaultListSelectEffect() {
304        return isUsingDefaultListSelectEffect() && getSelectEffectEnabled();
305    }
306
307    /**
308     * Sets the recycled pool size for the given presenter.
309     */
310    public void setRecycledPoolSize(Presenter presenter, int size) {
311        mRecycledPoolSize.put(presenter, size);
312    }
313
314    /**
315     * Returns the recycled pool size for the given presenter.
316     */
317    public int getRecycledPoolSize(Presenter presenter) {
318        return mRecycledPoolSize.containsKey(presenter) ? mRecycledPoolSize.get(presenter) :
319                DEFAULT_RECYCLED_POOL_SIZE;
320    }
321
322    /**
323     * Set {@link PresenterSelector} used for showing a select object in a hover card.
324     */
325    public final void setHoverCardPresenterSelector(PresenterSelector selector) {
326        mHoverCardPresenterSelector = selector;
327    }
328
329    /**
330     * Get {@link PresenterSelector} used for showing a select object in a hover card.
331     */
332    public final PresenterSelector getHoverCardPresenterSelector() {
333        return mHoverCardPresenterSelector;
334    }
335
336    /*
337     * Perform operations when a child of horizontal grid view is selected.
338     */
339    private void selectChildView(ViewHolder rowViewHolder, View view) {
340        if (view != null) {
341            if (rowViewHolder.mExpanded && rowViewHolder.mSelected) {
342                ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
343                        rowViewHolder.mGridView.getChildViewHolder(view);
344
345                if (mHoverCardPresenterSelector != null) {
346                    rowViewHolder.mHoverCardViewSwitcher.select(rowViewHolder.mGridView, view,
347                            ibh.mItem);
348                }
349                if (getOnItemViewSelectedListener() != null) {
350                    getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem,
351                            rowViewHolder, rowViewHolder.mRow);
352                }
353            }
354        } else {
355            if (mHoverCardPresenterSelector != null) {
356                rowViewHolder.mHoverCardViewSwitcher.unselect();
357            }
358            if (getOnItemViewSelectedListener() != null) {
359                getOnItemViewSelectedListener().onItemSelected(null, null,
360                        rowViewHolder, rowViewHolder.mRow);
361            }
362        }
363    }
364
365    private static void initStatics(Context context) {
366        if (sSelectedRowTopPadding == 0) {
367            sSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
368                    R.dimen.lb_browse_selected_row_top_padding);
369            sExpandedSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
370                    R.dimen.lb_browse_expanded_selected_row_top_padding);
371            sExpandedRowNoHovercardBottomPadding = context.getResources().getDimensionPixelSize(
372                    R.dimen.lb_browse_expanded_row_no_hovercard_bottom_padding);
373        }
374    }
375
376    private int getSpaceUnderBaseline(ListRowPresenter.ViewHolder vh) {
377        RowHeaderPresenter.ViewHolder headerViewHolder = vh.getHeaderViewHolder();
378        if (headerViewHolder != null) {
379            if (getHeaderPresenter() != null) {
380                return getHeaderPresenter().getSpaceUnderBaseline(headerViewHolder);
381            }
382            return headerViewHolder.view.getPaddingBottom();
383        }
384        return 0;
385    }
386
387    private void setVerticalPadding(ListRowPresenter.ViewHolder vh) {
388        int paddingTop, paddingBottom;
389        // Note: sufficient bottom padding needed for card shadows.
390        if (vh.isExpanded()) {
391            int headerSpaceUnderBaseline = getSpaceUnderBaseline(vh);
392            if (DEBUG) Log.v(TAG, "headerSpaceUnderBaseline " + headerSpaceUnderBaseline);
393            paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop) -
394                    headerSpaceUnderBaseline;
395            paddingBottom = mHoverCardPresenterSelector == null ?
396                    sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom;
397        } else if (vh.isSelected()) {
398            paddingTop = sSelectedRowTopPadding - vh.mPaddingBottom;
399            paddingBottom = sSelectedRowTopPadding;
400        } else {
401            paddingTop = 0;
402            paddingBottom = vh.mPaddingBottom;
403        }
404        vh.getGridView().setPadding(vh.mPaddingLeft, paddingTop, vh.mPaddingRight,
405                paddingBottom);
406    }
407
408    @Override
409    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
410        initStatics(parent.getContext());
411        ListRowView rowView = new ListRowView(parent.getContext());
412        setupFadingEffect(rowView);
413        if (mRowHeight != 0) {
414            rowView.getGridView().setRowHeight(mRowHeight);
415        }
416        return new ViewHolder(rowView, rowView.getGridView(), this);
417    }
418
419    /**
420     * Dispatch item selected event using current selected item in the {@link HorizontalGridView}.
421     * The method should only be called from onRowViewSelected().
422     */
423    @Override
424    protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) {
425        ViewHolder vh = (ViewHolder)holder;
426        ItemBridgeAdapter.ViewHolder itemViewHolder = (ItemBridgeAdapter.ViewHolder)
427                vh.mGridView.findViewHolderForPosition(vh.mGridView.getSelectedPosition());
428        if (itemViewHolder == null) {
429            super.dispatchItemSelectedListener(holder, selected);
430            return;
431        }
432
433        if (selected) {
434            if (getOnItemViewSelectedListener() != null) {
435                getOnItemViewSelectedListener().onItemSelected(
436                        itemViewHolder.getViewHolder(), itemViewHolder.mItem, vh, vh.getRow());
437            }
438        }
439    }
440
441    @Override
442    protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
443        super.onRowViewSelected(holder, selected);
444        ViewHolder vh = (ViewHolder) holder;
445        setVerticalPadding(vh);
446        updateFooterViewSwitcher(vh);
447    }
448
449    /*
450     * Show or hide hover card when row selection or expanded state is changed.
451     */
452    private void updateFooterViewSwitcher(ViewHolder vh) {
453        if (vh.mExpanded && vh.mSelected) {
454            if (mHoverCardPresenterSelector != null) {
455                vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view,
456                        mHoverCardPresenterSelector);
457            }
458            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
459                    vh.mGridView.findViewHolderForPosition(
460                            vh.mGridView.getSelectedPosition());
461            selectChildView(vh, ibh == null ? null : ibh.itemView);
462        } else {
463            if (mHoverCardPresenterSelector != null) {
464                vh.mHoverCardViewSwitcher.unselect();
465            }
466        }
467    }
468
469    private void setupFadingEffect(ListRowView rowView) {
470        // content is completely faded at 1/2 padding of left, fading length is 1/2 of padding.
471        HorizontalGridView gridView = rowView.getGridView();
472        if (mBrowseRowsFadingEdgeLength < 0) {
473            TypedArray ta = gridView.getContext()
474                    .obtainStyledAttributes(R.styleable.LeanbackTheme);
475            mBrowseRowsFadingEdgeLength = (int) ta.getDimension(
476                    R.styleable.LeanbackTheme_browseRowsFadingEdgeLength, 0);
477            ta.recycle();
478        }
479        gridView.setFadingLeftEdgeLength(mBrowseRowsFadingEdgeLength);
480    }
481
482    @Override
483    protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) {
484        super.onRowViewExpanded(holder, expanded);
485        ViewHolder vh = (ViewHolder) holder;
486        if (getRowHeight() != getExpandedRowHeight()) {
487            int newHeight = expanded ? getExpandedRowHeight() : getRowHeight();
488            vh.getGridView().setRowHeight(newHeight);
489        }
490        setVerticalPadding(vh);
491        updateFooterViewSwitcher(vh);
492    }
493
494    @Override
495    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
496        super.onBindRowViewHolder(holder, item);
497        ViewHolder vh = (ViewHolder) holder;
498        ListRow rowItem = (ListRow) item;
499        vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
500        vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
501    }
502
503    @Override
504    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
505        ViewHolder vh = (ViewHolder) holder;
506        vh.mGridView.setAdapter(null);
507        vh.mItemBridgeAdapter.clear();
508        super.onUnbindRowViewHolder(holder);
509    }
510
511    /**
512     * ListRowPresenter overrides the default select effect of {@link RowPresenter}
513     * and return false.
514     */
515    @Override
516    public final boolean isUsingDefaultSelectEffect() {
517        return false;
518    }
519
520    /**
521     * Returns true so that default select effect is applied to each individual
522     * child of {@link HorizontalGridView}.  Subclass may return false to disable
523     * the default implementation.
524     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
525     */
526    public boolean isUsingDefaultListSelectEffect() {
527        return true;
528    }
529
530    /**
531     * Returns true if SDK >= 18, where default shadow
532     * is applied to each individual child of {@link HorizontalGridView}.
533     * Subclass may return false to disable.
534     */
535    public boolean isUsingDefaultShadow() {
536        return ShadowOverlayContainer.supportsShadow();
537    }
538
539    /**
540     * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
541     * on each child of horizontal list.   If subclass returns false in isUsingDefaultShadow()
542     * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
543     */
544    public boolean isUsingZOrder() {
545        return ShadowHelper.getInstance().usesZShadow();
546    }
547
548    /**
549     * Enable or disable child shadow.
550     * This is not only for enable/disable default shadow implementation but also subclass must
551     * respect this flag.
552     */
553    public final void setShadowEnabled(boolean enabled) {
554        mShadowEnabled = enabled;
555    }
556
557    /**
558     * Returns true if child shadow is enabled.
559     * This is not only for enable/disable default shadow implementation but also subclass must
560     * respect this flag.
561     */
562    public final boolean getShadowEnabled() {
563        return mShadowEnabled;
564    }
565
566    /**
567     * Enables or disabled rounded corners on children of this row.
568     * Supported on Android SDK >= L.
569     */
570    public final void enableChildRoundedCorners(boolean enable) {
571        mRoundedCornersEnabled = enable;
572    }
573
574    /**
575     * Returns true if rounded corners are enabled for children of this row.
576     */
577    public final boolean areChildRoundedCornersEnabled() {
578        return mRoundedCornersEnabled;
579    }
580
581    final boolean needsDefaultShadow() {
582        return isUsingDefaultShadow() && getShadowEnabled();
583    }
584
585    @Override
586    public boolean canDrawOutOfBounds() {
587        return needsDefaultShadow();
588    }
589
590    /**
591     * Applies select level to header and draw a default color dim over each child
592     * of {@link HorizontalGridView}.
593     * <p>
594     * Subclass may override this method.  A subclass
595     * needs to call super.onSelectLevelChanged() for applying header select level
596     * and optionally applying a default select level to each child view of
597     * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()}
598     * is true.  Subclass may override {@link #isUsingDefaultListSelectEffect()} to return
599     * false and deal with the individual item select level by itself.
600     * </p>
601     */
602    @Override
603    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
604        super.onSelectLevelChanged(holder);
605        if (needsDefaultListSelectEffect()) {
606            ViewHolder vh = (ViewHolder) holder;
607            int dimmedColor = vh.mColorDimmer.getPaint().getColor();
608            for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
609                ShadowOverlayContainer wrapper = (ShadowOverlayContainer) vh.mGridView.getChildAt(i);
610                wrapper.setOverlayColor(dimmedColor);
611            }
612            if (vh.mGridView.getFadingLeftEdge()) {
613                vh.mGridView.invalidate();
614            }
615        }
616    }
617
618    @Override
619    public void freeze(RowPresenter.ViewHolder holder, boolean freeze) {
620        ViewHolder vh = (ViewHolder) holder;
621        vh.mGridView.setScrollEnabled(!freeze);
622    }
623
624    @Override
625    public void setEntranceTransitionState(RowPresenter.ViewHolder holder,
626            boolean afterEntrance) {
627        super.setEntranceTransitionState(holder, afterEntrance);
628        ((ViewHolder) holder).mGridView.setChildrenVisibility(
629                afterEntrance? View.VISIBLE : View.INVISIBLE);
630    }
631}
632