ListRowPresenter.java revision cf94c5fa8ae8edb7e26a623133207415ceeed187
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 java.util.ArrayList;
17
18import android.graphics.Canvas;
19import android.support.v17.leanback.graphics.ColorOverlayDimmer;
20import android.support.v17.leanback.widget.Presenter.ViewHolder;
21import android.support.v7.widget.RecyclerView;
22import android.view.View;
23import android.view.ViewGroup;
24
25/**
26 * Presents a ListRow using a {@link HorizontalGridView} hosted in a {@link BrowseRowView}.
27 * <p>
28 * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to
29 * display a view for the currently focused list item below the rendered
30 * list. This view is known as a hover card.
31 * </p>
32 * <p>
33 * ListRowPresenter has the same capability of {@link #setHeaderPresenter(RowHeaderPresenter)}
34 * as parent class {@link RowPresenter}.
35 * </p>
36 */
37public class ListRowPresenter extends RowPresenter {
38
39    private static final String TAG = "ListRowPresenter";
40    private static final boolean DEBUG = false;
41
42    public static class ViewHolder extends RowPresenter.ViewHolder {
43        final ListRowPresenter mListRowPresenter;
44        final HorizontalGridView mGridView;
45        final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter();
46        final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
47        final ColorOverlayDimmer mColorDimmer;
48
49        public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) {
50            super(rootView);
51            mGridView = gridView;
52            mListRowPresenter = p;
53            mColorDimmer = ColorOverlayDimmer.createDefault(rootView.getContext());
54        }
55
56        public final ListRowPresenter getListRowPresenter() {
57            return mListRowPresenter;
58        }
59
60        public final HorizontalGridView getGridView() {
61            return mGridView;
62        }
63    }
64
65    private PresenterSelector mHoverCardPresenterSelector;
66
67    @Override
68    protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
69        super.initializeRowViewHolder(holder);
70        final ViewHolder rowViewHolder = (ViewHolder) holder;
71        if (needsDefaultListItemDecoration()) {
72            rowViewHolder.mGridView.addItemDecoration(new ItemDecoration(rowViewHolder));
73        }
74        FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter);
75        rowViewHolder.mGridView.setOnChildSelectedListener(
76                new OnChildSelectedListener() {
77            @Override
78            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
79                selectChildView(rowViewHolder, view);
80            }
81        });
82        if (getOnItemClickedListener() != null) {
83            // Only when having an OnItemClickListner, we will attach the OnClickListener.
84            rowViewHolder.mItemBridgeAdapter.setAdapterListener(
85                    new ItemBridgeAdapter.AdapterListener() {
86                @Override
87                public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
88                    viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
89                        @Override
90                        public void onClick(View v) {
91                            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
92                                    rowViewHolder.mGridView.getChildViewHolder(v);
93                            if (getOnItemClickedListener() != null) {
94                                getOnItemClickedListener().onItemClicked(ibh.mItem,
95                                        (ListRow) rowViewHolder.mRow);
96                            }
97                        }
98                    });
99                }
100            });
101        }
102    }
103
104    private boolean needsDefaultListItemDecoration() {
105        return isUsingDefaultListSelectEffect() && getSelectEffectEnabled();
106    }
107
108    /**
109     * Set {@link PresenterSelector} used for showing a select object in a hover card.
110     */
111    public final void setHoverCardPresenterSelector(PresenterSelector selector) {
112        mHoverCardPresenterSelector = selector;
113    }
114
115    /**
116     * Get {@link PresenterSelector} used for showing a select object in a hover card.
117     */
118    public final PresenterSelector getHoverCardPresenterSelector() {
119        return mHoverCardPresenterSelector;
120    }
121
122    /*
123     * Perform operations when a child of horizontal grid view is selected.
124     */
125    private void selectChildView(ViewHolder rowViewHolder, View view) {
126        ItemBridgeAdapter.ViewHolder ibh = null;
127        if (view != null) {
128            ibh = (ItemBridgeAdapter.ViewHolder)
129                    rowViewHolder.mGridView.getChildViewHolder(view);
130        }
131        if (view == null) {
132            if (mHoverCardPresenterSelector != null) {
133                rowViewHolder.mHoverCardViewSwitcher.unselect();
134            }
135            if (getOnItemSelectedListener() != null) {
136                getOnItemSelectedListener().onItemSelected(null, rowViewHolder.mRow);
137            }
138        } else if (rowViewHolder.mExpanded && rowViewHolder.mSelected) {
139            if (mHoverCardPresenterSelector != null) {
140                rowViewHolder.mHoverCardViewSwitcher.select(rowViewHolder.mGridView, view,
141                        ibh.mItem);
142            }
143            if (getOnItemSelectedListener() != null) {
144                getOnItemSelectedListener().onItemSelected(ibh.mItem, rowViewHolder.mRow);
145            }
146        }
147    }
148
149    @Override
150    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
151        BrowseRowView rowView = new BrowseRowView(parent.getContext());
152        return new ViewHolder(rowView, rowView.getGridView(), this);
153    }
154
155    @Override
156    protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
157        updateFooterViewSwitcher((ViewHolder) holder);
158        updateInitialChildSelection((ViewHolder) holder);
159    }
160
161    /*
162     * Show or hide hover card when row selection or expanded state is changed.
163     */
164    private void updateFooterViewSwitcher(ViewHolder vh) {
165        if (vh.mExpanded && vh.mSelected) {
166            if (mHoverCardPresenterSelector != null) {
167                vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view,
168                        mHoverCardPresenterSelector);
169            }
170        } else {
171            if (mHoverCardPresenterSelector != null) {
172                vh.mHoverCardViewSwitcher.clear();
173            }
174        }
175    }
176
177    /*
178     * Make initial child selection when row selection state is changed.
179     */
180    private void updateInitialChildSelection(ViewHolder vh) {
181        if (vh.mExpanded && vh.mSelected) {
182            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
183                    vh.mGridView.findViewHolderForPosition(
184                            vh.mGridView.getSelectedPosition());
185            selectChildView(vh, ibh == null ? null : ibh.mHolder.view);
186        } else {
187            selectChildView(vh, null);
188        }
189    }
190
191    @Override
192    protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) {
193        super.onRowViewExpanded(holder, expanded);
194        ViewHolder vh = (ViewHolder) holder;
195        vh.mGridView.setClipToPadding(!expanded);
196        updateFooterViewSwitcher(vh);
197    }
198
199    @Override
200    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
201        super.onBindViewHolder(holder, item);
202        ViewHolder vh = (ViewHolder)holder;
203        ListRow rowItem = (ListRow) item;
204        vh.mItemBridgeAdapter.clear();
205        vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
206        vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
207    }
208
209    @Override
210    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
211        ViewHolder vh = (ViewHolder)holder;
212        vh.mGridView.setAdapter(null);
213        super.onUnbindViewHolder(holder);
214    }
215
216    /**
217     * ListRowPresenter overrides the default select effect of {@link RowPresenter}
218     * and return false.
219     */
220    @Override
221    public final boolean isUsingDefaultSelectEffect() {
222        return false;
223    }
224
225    /**
226     * Returns true so that default select effect is applied to each individual
227     * child of {@link HorizontalGridView}.  Subclass may return false to disable
228     * the default implementation.
229     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
230     */
231    public boolean isUsingDefaultListSelectEffect() {
232        return true;
233    }
234
235    /**
236     * Applies select level to header and draw a default color dim over each child
237     * of {@link HorizontalGridView}.
238     * <p>
239     * Subclass may override this method.  A subclass
240     * needs to call super.onSelectLevelChanged() for applying header select level
241     * and optionally applying a default select level to each child view of
242     * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()}
243     * is true.  Subclass may override {@link #isUsingDefaultListSelectEffect()} to return
244     * false and deal with the individual item select level by itself.
245     * </p>
246     */
247    @Override
248    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
249        super.onSelectLevelChanged(holder);
250        if (needsDefaultListItemDecoration()) {
251            ViewHolder vh = (ViewHolder) holder;
252            vh.mColorDimmer.setActiveLevel(holder.mSelectLevel);
253            vh.mGridView.invalidate();
254        }
255    }
256
257    private void drawDimSelectionForChildren(ViewHolder vh, Canvas c) {
258        final ColorOverlayDimmer dimmer = vh.mColorDimmer;
259        if (dimmer.needsDraw()) {
260            final HorizontalGridView gridView = vh.mGridView;
261            // Clip to padding when not expanded
262            if (!vh.mExpanded) {
263                c.clipRect(gridView.getPaddingLeft(), gridView.getPaddingTop(),
264                        gridView.getWidth() - gridView.getPaddingRight(),
265                        gridView.getHeight() - gridView.getPaddingBottom());
266            }
267            for (int i = 0, count = gridView.getChildCount(); i < count; i++) {
268                dimmer.drawColorOverlay(c, gridView.getChildAt(i), true);
269            }
270        }
271    }
272
273    final class ItemDecoration extends RecyclerView.ItemDecoration {
274        ViewHolder mViewHolder;
275        ItemDecoration(ViewHolder viewHolder) {
276            mViewHolder = viewHolder;
277        }
278        @Override
279        public void onDrawOver(Canvas c, RecyclerView parent) {
280            drawDimSelectionForChildren(mViewHolder, c);
281        }
282    }
283
284}
285