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