VerticalGridPresenter.java revision a37118706af7d79adeb8b3918fb52fad99ece361
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.support.v17.leanback.R;
17import android.view.LayoutInflater;
18import android.view.View;
19import android.view.ViewGroup;
20import android.view.ViewGroup.LayoutParams;
21import android.util.Log;
22
23/**
24 * A presenter that renders objects in a vertical grid.
25 *
26 */
27public class VerticalGridPresenter extends Presenter {
28    private static final String TAG = "GridPresenter";
29    private static final boolean DEBUG = false;
30
31    public static class ViewHolder extends Presenter.ViewHolder {
32        final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter();
33        final VerticalGridView mGridView;
34        boolean mInitialized;
35
36        public ViewHolder(VerticalGridView view) {
37            super(view);
38            mGridView = view;
39        }
40
41        public VerticalGridView getGridView() {
42            return mGridView;
43        }
44    }
45
46    private int mNumColumns = -1;
47    private int mZoomFactor;
48    private boolean mShadowEnabled = true;
49    private OnItemClickedListener mOnItemClickedListener;
50    private OnItemSelectedListener mOnItemSelectedListener;
51    private OnItemViewSelectedListener mOnItemViewSelectedListener;
52    private OnItemViewClickedListener mOnItemViewClickedListener;
53    private boolean mRoundedCornersEnabled = true;
54
55    public VerticalGridPresenter() {
56        this(FocusHighlight.ZOOM_FACTOR_LARGE);
57    }
58
59    public VerticalGridPresenter(int zoomFactor) {
60        mZoomFactor = zoomFactor;
61    }
62
63    /**
64     * Sets the number of columns in the vertical grid.
65     */
66    public void setNumberOfColumns(int numColumns) {
67        if (numColumns < 0) {
68            throw new IllegalArgumentException("Invalid number of columns");
69        }
70        if (mNumColumns != numColumns) {
71            mNumColumns = numColumns;
72        }
73    }
74
75    /**
76     * Returns the number of columns in the vertical grid.
77     */
78    public int getNumberOfColumns() {
79        return mNumColumns;
80    }
81
82    /**
83     * Enable or disable child shadow.
84     * This is not only for enable/disable default shadow implementation but also subclass must
85     * respect this flag.
86     */
87    public final void setShadowEnabled(boolean enabled) {
88        mShadowEnabled = enabled;
89    }
90
91    /**
92     * Returns true if child shadow is enabled.
93     * This is not only for enable/disable default shadow implementation but also subclass must
94     * respect this flag.
95     */
96    public final boolean getShadowEnabled() {
97        return mShadowEnabled;
98    }
99
100    /**
101     * Returns true if opticalBounds is supported (SDK >= 18) so that default shadow
102     * is applied to each individual child of {@link VerticalGridView}.
103     * Subclass may return false to disable.
104     */
105    public boolean isUsingDefaultShadow() {
106        return ShadowOverlayContainer.supportsShadow();
107    }
108
109    /**
110     * Enables or disabled rounded corners on children of this row.
111     * Supported on Android SDK >= L.
112     */
113    public final void enableChildRoundedCorners(boolean enable) {
114        mRoundedCornersEnabled = enable;
115    }
116
117    /**
118     * Returns true if rounded corners are enabled for children of this row.
119     */
120    public final boolean areChildRoundedCornersEnabled() {
121        return mRoundedCornersEnabled;
122    }
123
124    /**
125     * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
126     * on each child of vertical grid.   If subclass returns false in isUsingDefaultShadow()
127     * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
128     */
129    public boolean isUsingZOrder() {
130        return ShadowHelper.getInstance().usesZShadow();
131    }
132
133    final boolean needsDefaultShadow() {
134        return isUsingDefaultShadow() && getShadowEnabled();
135    }
136
137    @Override
138    public final ViewHolder onCreateViewHolder(ViewGroup parent) {
139        ViewHolder vh = createGridViewHolder(parent);
140        vh.mInitialized = false;
141        initializeGridViewHolder(vh);
142        if (!vh.mInitialized) {
143            throw new RuntimeException("super.initializeGridViewHolder() must be called");
144        }
145        return vh;
146    }
147
148    /**
149     * Subclass may override this to inflate a different layout.
150     */
151    protected ViewHolder createGridViewHolder(ViewGroup parent) {
152        View root = LayoutInflater.from(parent.getContext()).inflate(
153                R.layout.lb_vertical_grid, parent, false);
154        return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid));
155    }
156
157    private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
158        @Override
159        public View createWrapper(View root) {
160            ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext());
161            wrapper.setLayoutParams(
162                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
163            wrapper.initialize(needsDefaultShadow(), false, areChildRoundedCornersEnabled());
164            return wrapper;
165        }
166        @Override
167        public void wrap(View wrapper, View wrapped) {
168            ((ShadowOverlayContainer) wrapper).wrap(wrapped);
169        }
170    };
171
172    /**
173     * Called after a {@link VerticalGridPresenter.ViewHolder} is created.
174     * Subclasses may override this method and start by calling
175     * super.initializeGridViewHolder(ViewHolder).
176     *
177     * @param vh The ViewHolder to initialize for the vertical grid.
178     */
179    protected void initializeGridViewHolder(ViewHolder vh) {
180        if (mNumColumns == -1) {
181            throw new IllegalStateException("Number of columns must be set");
182        }
183        if (DEBUG) Log.v(TAG, "mNumColumns " + mNumColumns);
184        vh.getGridView().setNumColumns(mNumColumns);
185        vh.mInitialized = true;
186
187        if (needsDefaultShadow() || areChildRoundedCornersEnabled()) {
188            vh.mItemBridgeAdapter.setWrapper(mWrapper);
189            ShadowOverlayContainer.prepareParentForShadow(vh.getGridView());
190            ((ViewGroup) vh.view).setClipChildren(false);
191        }
192        vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder());
193        FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, mZoomFactor);
194
195        final ViewHolder gridViewHolder = vh;
196        vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
197            @Override
198            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
199                selectChildView(gridViewHolder, view);
200            }
201        });
202
203        vh.mItemBridgeAdapter.setAdapterListener(new ItemBridgeAdapter.AdapterListener() {
204            @Override
205            public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
206                // Only when having an OnItemClickListner, we attach the OnClickListener.
207                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
208                    final View itemView = itemViewHolder.mHolder.view;
209                    itemView.setOnClickListener(new View.OnClickListener() {
210                        @Override
211                        public void onClick(View view) {
212                            if (getOnItemClickedListener() != null) {
213                                // Row is always null
214                                getOnItemClickedListener().onItemClicked(itemViewHolder.mItem,
215                                        null);
216                            }
217                            if (getOnItemViewClickedListener() != null) {
218                                // Row is always null
219                                getOnItemViewClickedListener().onItemClicked(
220                                        itemViewHolder.mHolder, itemViewHolder.mItem, null, null);
221                            }
222                        }
223                    });
224                }
225            }
226
227            @Override
228            public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
229                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
230                    viewHolder.mHolder.view.setOnClickListener(null);
231                }
232            }
233
234            @Override
235            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
236                viewHolder.itemView.setActivated(true);
237            }
238        });
239    }
240
241    @Override
242    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
243        if (DEBUG) Log.v(TAG, "onBindViewHolder " + item);
244        ViewHolder vh = (ViewHolder) viewHolder;
245        vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item);
246        vh.getGridView().setAdapter(vh.mItemBridgeAdapter);
247    }
248
249    @Override
250    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
251        if (DEBUG) Log.v(TAG, "onUnbindViewHolder");
252        ViewHolder vh = (ViewHolder) viewHolder;
253        vh.mItemBridgeAdapter.setAdapter(null);
254        vh.getGridView().setAdapter(null);
255    }
256
257    /**
258     * Sets the item selected listener.
259     * Since this is a grid the row parameter is always null.
260     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
261     */
262    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
263        mOnItemSelectedListener = listener;
264    }
265
266    /**
267     * Returns the item selected listener.
268     * @deprecated Use {@link #getOnItemViewSelectedListener()}
269     */
270    public final OnItemSelectedListener getOnItemSelectedListener() {
271        return mOnItemSelectedListener;
272    }
273
274    /**
275     * Sets the item selected listener.
276     * Since this is a grid the row parameter is always null.
277     */
278    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
279        mOnItemViewSelectedListener = listener;
280    }
281
282    /**
283     * Returns the item selected listener.
284     */
285    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
286        return mOnItemViewSelectedListener;
287    }
288
289    /**
290     * Sets the item clicked listener.
291     * OnItemClickedListener will override {@link View.OnClickListener} that
292     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
293     * So in general, developer should choose one of the listeners but not both.
294     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
295     */
296    public final void setOnItemClickedListener(OnItemClickedListener listener) {
297        mOnItemClickedListener = listener;
298    }
299
300    /**
301     * Sets the item clicked listener.
302     * OnItemViewClickedListener will override {@link View.OnClickListener} that
303     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
304     * So in general, developer should choose one of the listeners but not both.
305     */
306    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
307        mOnItemViewClickedListener = listener;
308    }
309
310    /**
311     * Returns the item clicked listener.
312     * @deprecated Use {@link #getOnItemViewClickedListener()}
313     */
314    public final OnItemClickedListener getOnItemClickedListener() {
315        return mOnItemClickedListener;
316    }
317
318    /**
319     * Returns the item clicked listener.
320     */
321    public final OnItemViewClickedListener getOnItemViewClickedListener() {
322        return mOnItemViewClickedListener;
323    }
324
325    private void selectChildView(ViewHolder vh, View view) {
326        if (getOnItemSelectedListener() != null) {
327            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
328                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
329            if (ibh == null) {
330                getOnItemSelectedListener().onItemSelected(null, null);
331            } else {
332                getOnItemSelectedListener().onItemSelected(ibh.mItem, null);
333            }
334        }
335        if (getOnItemViewSelectedListener() != null) {
336            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
337                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
338            if (ibh == null) {
339                getOnItemViewSelectedListener().onItemSelected(null, null, null, null);
340            } else {
341                getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null);
342            }
343        }
344    }
345}
346