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