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