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