VerticalGridPresenter.java revision 0d734cb6671b314f84cb4911cd37631177d07fba
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    final boolean needsDefaultShadow() {
107        return isUsingDefaultShadow() && getShadowEnabled();
108    }
109
110    @Override
111    public final ViewHolder onCreateViewHolder(ViewGroup parent) {
112        ViewHolder vh = createGridViewHolder(parent);
113        vh.mInitialized = false;
114        initializeGridViewHolder(vh);
115        if (!vh.mInitialized) {
116            throw new RuntimeException("super.initializeGridViewHolder() must be called");
117        }
118        return vh;
119    }
120
121    /**
122     * Subclass may override this to inflate a different layout.
123     */
124    protected ViewHolder createGridViewHolder(ViewGroup parent) {
125        View root = LayoutInflater.from(parent.getContext()).inflate(
126                R.layout.lb_browse_grid, parent, false);
127        return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid));
128    }
129
130    private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
131        @Override
132        public View createWrapper(View root) {
133            ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext());
134            wrapper.setLayoutParams(
135                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
136            wrapper.initialize(needsDefaultShadow(), false);
137            return wrapper;
138        }
139        @Override
140        public void wrap(View wrapper, View wrapped) {
141            ((ShadowOverlayContainer) wrapper).wrap(wrapped);
142        }
143    };
144
145    protected void initializeGridViewHolder(ViewHolder vh) {
146        if (mNumColumns == -1) {
147            throw new IllegalStateException("Number of columns must be set");
148        }
149        if (DEBUG) Log.v(TAG, "mNumColumns " + mNumColumns);
150        vh.getGridView().setNumColumns(mNumColumns);
151        vh.mInitialized = true;
152
153        if (needsDefaultShadow()) {
154            vh.mItemBridgeAdapter.setWrapper(mWrapper);
155            ShadowOverlayContainer.prepareParentForShadow(vh.getGridView());
156            ((ViewGroup) vh.view).setClipChildren(false);
157        }
158        FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, mZoomFactor);
159
160        final ViewHolder gridViewHolder = vh;
161        vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
162            @Override
163            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
164                selectChildView(gridViewHolder, view);
165            }
166        });
167
168        vh.mItemBridgeAdapter.setAdapterListener(new ItemBridgeAdapter.AdapterListener() {
169            @Override
170            public void onCreate(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
171                // Only when having an OnItemClickListner, we attach the OnClickListener.
172                if (getOnItemClickedListener() != null) {
173                    final View itemView = itemViewHolder.getViewHolder().view;
174                    itemView.setOnClickListener(new View.OnClickListener() {
175                        @Override
176                        public void onClick(View view) {
177                            if (getOnItemClickedListener() != null) {
178                                // Row is always null
179                                getOnItemClickedListener().onItemClicked(itemViewHolder.mItem, null);
180                            }
181                        }
182                    });
183                }
184            }
185        });
186    }
187
188    @Override
189    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
190        if (DEBUG) Log.v(TAG, "onBindViewHolder " + item);
191        ViewHolder vh = (ViewHolder) viewHolder;
192        vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item);
193        vh.getGridView().setAdapter(vh.mItemBridgeAdapter);
194    }
195
196    @Override
197    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
198        if (DEBUG) Log.v(TAG, "onUnbindViewHolder");
199        ViewHolder vh = (ViewHolder) viewHolder;
200        vh.mItemBridgeAdapter.setAdapter(null);
201        vh.getGridView().setAdapter(null);
202    }
203
204    /**
205     * Sets the item selected listener.
206     * Since this is a grid the row parameter is always null.
207     */
208    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
209        mOnItemSelectedListener = listener;
210    }
211
212    /**
213     * Returns the item selected listener.
214     */
215    public final OnItemSelectedListener getOnItemSelectedListener() {
216        return mOnItemSelectedListener;
217    }
218
219    /**
220     * Sets the item clicked listener.
221     * OnItemClickedListener will override {@link View.OnClickListener} that
222     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
223     * So in general, developer should choose one of the listeners but not both.
224     */
225    public final void setOnItemClickedListener(OnItemClickedListener listener) {
226        mOnItemClickedListener = listener;
227    }
228
229    /**
230     * Returns the item clicked listener.
231     */
232    public final OnItemClickedListener getOnItemClickedListener() {
233        return mOnItemClickedListener;
234    }
235
236    private void selectChildView(ViewHolder vh, View view) {
237        if (getOnItemSelectedListener() != null) {
238            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
239                (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
240
241            getOnItemSelectedListener().onItemSelected(ibh == null ? null : ibh.mItem, null);
242        }
243    };
244}
245