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