VerticalGridPresenter.java revision 4f34a05cdf73b68c3b2eb8678f740ab15225126a
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_MEDIUM);
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 onCreate(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.getViewHolder().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 onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
229                viewHolder.itemView.setActivated(true);
230            }
231        });
232    }
233
234    @Override
235    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
236        if (DEBUG) Log.v(TAG, "onBindViewHolder " + item);
237        ViewHolder vh = (ViewHolder) viewHolder;
238        vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item);
239        vh.getGridView().setAdapter(vh.mItemBridgeAdapter);
240    }
241
242    @Override
243    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
244        if (DEBUG) Log.v(TAG, "onUnbindViewHolder");
245        ViewHolder vh = (ViewHolder) viewHolder;
246        vh.mItemBridgeAdapter.setAdapter(null);
247        vh.getGridView().setAdapter(null);
248    }
249
250    /**
251     * Sets the item selected listener.
252     * Since this is a grid the row parameter is always null.
253     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
254     */
255    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
256        mOnItemSelectedListener = listener;
257    }
258
259    /**
260     * Returns the item selected listener.
261     * @deprecated Use {@link #getOnItemViewSelectedListener()}
262     */
263    public final OnItemSelectedListener getOnItemSelectedListener() {
264        return mOnItemSelectedListener;
265    }
266
267    /**
268     * Sets the item selected listener.
269     * Since this is a grid the row parameter is always null.
270     */
271    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
272        mOnItemViewSelectedListener = listener;
273    }
274
275    /**
276     * Returns the item selected listener.
277     */
278    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
279        return mOnItemViewSelectedListener;
280    }
281
282    /**
283     * Sets the item clicked listener.
284     * OnItemClickedListener will override {@link View.OnClickListener} that
285     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
286     * So in general, developer should choose one of the listeners but not both.
287     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
288     */
289    public final void setOnItemClickedListener(OnItemClickedListener listener) {
290        mOnItemClickedListener = listener;
291    }
292
293    /**
294     * Sets the item clicked listener.
295     * OnItemViewClickedListener will override {@link View.OnClickListener} that
296     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
297     * So in general, developer should choose one of the listeners but not both.
298     */
299    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
300        mOnItemViewClickedListener = listener;
301    }
302
303    /**
304     * Returns the item clicked listener.
305     * @deprecated Use {@link #getOnItemViewClickedListener()}
306     */
307    public final OnItemClickedListener getOnItemClickedListener() {
308        return mOnItemClickedListener;
309    }
310
311    /**
312     * Returns the item clicked listener.
313     */
314    public final OnItemViewClickedListener getOnItemViewClickedListener() {
315        return mOnItemViewClickedListener;
316    }
317
318    private void selectChildView(ViewHolder vh, View view) {
319        if (getOnItemSelectedListener() != null) {
320            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
321                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
322            if (ibh == null) {
323                getOnItemSelectedListener().onItemSelected(null, null);
324            } else {
325                getOnItemSelectedListener().onItemSelected(ibh.mItem, null);
326            }
327        }
328        if (getOnItemViewSelectedListener() != null) {
329            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
330                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
331            if (ibh == null) {
332                getOnItemViewSelectedListener().onItemSelected(null, null, null, null);
333            } else {
334                getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null);
335            }
336        }
337    }
338}
339