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