VerticalGridPresenter.java revision afe1da48b4673f64c67c130237dd485a34a69c1b
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     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
238     */
239    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
240        mOnItemSelectedListener = listener;
241    }
242
243    /**
244     * Returns the item selected listener.
245     * @deprecated Use {@link #getOnItemViewSelectedListener()}
246     */
247    public final OnItemSelectedListener getOnItemSelectedListener() {
248        return mOnItemSelectedListener;
249    }
250
251    /**
252     * Sets the item selected listener.
253     * Since this is a grid the row parameter is always null.
254     */
255    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
256        mOnItemViewSelectedListener = listener;
257    }
258
259    /**
260     * Returns the item selected listener.
261     */
262    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
263        return mOnItemViewSelectedListener;
264    }
265
266    /**
267     * Sets the item clicked listener.
268     * OnItemClickedListener will override {@link View.OnClickListener} that
269     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
270     * So in general, developer should choose one of the listeners but not both.
271     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
272     */
273    public final void setOnItemClickedListener(OnItemClickedListener listener) {
274        mOnItemClickedListener = listener;
275    }
276
277    /**
278     * Sets the item clicked listener.
279     * OnItemViewClickedListener will override {@link View.OnClickListener} that
280     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
281     * So in general, developer should choose one of the listeners but not both.
282     */
283    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
284        mOnItemViewClickedListener = listener;
285    }
286
287    /**
288     * Returns the item clicked listener.
289     * @deprecated Use {@link #getOnItemViewClickedListener()}
290     */
291    public final OnItemClickedListener getOnItemClickedListener() {
292        return mOnItemClickedListener;
293    }
294
295    /**
296     * Returns the item clicked listener.
297     */
298    public final OnItemViewClickedListener getOnItemViewClickedListener() {
299        return mOnItemViewClickedListener;
300    }
301
302    private void selectChildView(ViewHolder vh, View view) {
303        if (getOnItemSelectedListener() != null) {
304            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
305                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
306            if (ibh == null) {
307                getOnItemSelectedListener().onItemSelected(null, null);
308            } else {
309                getOnItemSelectedListener().onItemSelected(ibh.mItem, null);
310            }
311        }
312        if (getOnItemViewSelectedListener() != null) {
313            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
314                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
315            if (ibh == null) {
316                getOnItemViewSelectedListener().onItemSelected(null, null, null, null);
317            } else {
318                getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null);
319            }
320        }
321    }
322}
323