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