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