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