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