• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
14 package android.support.v17.leanback.widget;
15 
16 import android.content.Context;
17 import android.support.v17.leanback.R;
18 import android.support.v17.leanback.system.Settings;
19 import android.support.v17.leanback.transition.TransitionHelper;
20 import android.util.Log;
21 import android.view.LayoutInflater;
22 import android.view.View;
23 import android.view.ViewGroup;
24 
25 /**
26  * A presenter that renders objects in a {@link VerticalGridView}.
27  */
28 public class VerticalGridPresenter extends Presenter {
29     private static final String TAG = "GridPresenter";
30     private static final boolean DEBUG = false;
31 
32     class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter {
33         @Override
onCreate(ItemBridgeAdapter.ViewHolder viewHolder)34         protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
35             if (viewHolder.itemView instanceof ViewGroup) {
36                 TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView,
37                         true);
38             }
39             if (mShadowOverlayHelper != null) {
40                 mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
41             }
42         }
43 
44         @Override
onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder)45         public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
46             // Only when having an OnItemClickListener, we attach the OnClickListener.
47             if (getOnItemViewClickedListener() != null) {
48                 final View itemView = itemViewHolder.mHolder.view;
49                 itemView.setOnClickListener(new View.OnClickListener() {
50                     @Override
51                     public void onClick(View view) {
52                         if (getOnItemViewClickedListener() != null) {
53                             // Row is always null
54                             getOnItemViewClickedListener().onItemClicked(
55                                     itemViewHolder.mHolder, itemViewHolder.mItem, null, null);
56                         }
57                     }
58                 });
59             }
60         }
61 
62         @Override
onUnbind(ItemBridgeAdapter.ViewHolder viewHolder)63         public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
64             if (getOnItemViewClickedListener() != null) {
65                 viewHolder.mHolder.view.setOnClickListener(null);
66             }
67         }
68 
69         @Override
onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder)70         public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
71             viewHolder.itemView.setActivated(true);
72         }
73     }
74 
75     /**
76      * ViewHolder for the VerticalGridPresenter.
77      */
78     public static class ViewHolder extends Presenter.ViewHolder {
79         ItemBridgeAdapter mItemBridgeAdapter;
80         final VerticalGridView mGridView;
81         boolean mInitialized;
82 
ViewHolder(VerticalGridView view)83         public ViewHolder(VerticalGridView view) {
84             super(view);
85             mGridView = view;
86         }
87 
getGridView()88         public VerticalGridView getGridView() {
89             return mGridView;
90         }
91     }
92 
93     private int mNumColumns = -1;
94     private int mFocusZoomFactor;
95     private boolean mUseFocusDimmer;
96     private boolean mShadowEnabled = true;
97     private boolean mKeepChildForeground = true;
98     private OnItemViewSelectedListener mOnItemViewSelectedListener;
99     private OnItemViewClickedListener mOnItemViewClickedListener;
100     private boolean mRoundedCornersEnabled = true;
101     ShadowOverlayHelper mShadowOverlayHelper;
102     private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper;
103 
104     /**
105      * Constructs a VerticalGridPresenter with defaults.
106      * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
107      * enabled dimming on focus.
108      */
VerticalGridPresenter()109     public VerticalGridPresenter() {
110         this(FocusHighlight.ZOOM_FACTOR_LARGE);
111     }
112 
113     /**
114      * Constructs a VerticalGridPresenter with the given parameters.
115      *
116      * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
117      *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
118      *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
119      *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
120      *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
121      *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
122      * enabled dimming on focus.
123      */
VerticalGridPresenter(int focusZoomFactor)124     public VerticalGridPresenter(int focusZoomFactor) {
125         this(focusZoomFactor, true);
126     }
127 
128     /**
129      * Constructs a VerticalGridPresenter with the given parameters.
130      *
131      * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
132      *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
133      *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
134      *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
135      *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
136      *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
137      * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
138      */
VerticalGridPresenter(int focusZoomFactor, boolean useFocusDimmer)139     public VerticalGridPresenter(int focusZoomFactor, boolean useFocusDimmer) {
140         mFocusZoomFactor = focusZoomFactor;
141         mUseFocusDimmer = useFocusDimmer;
142     }
143 
144     /**
145      * Sets the number of columns in the vertical grid.
146      */
setNumberOfColumns(int numColumns)147     public void setNumberOfColumns(int numColumns) {
148         if (numColumns < 0) {
149             throw new IllegalArgumentException("Invalid number of columns");
150         }
151         if (mNumColumns != numColumns) {
152             mNumColumns = numColumns;
153         }
154     }
155 
156     /**
157      * Returns the number of columns in the vertical grid.
158      */
getNumberOfColumns()159     public int getNumberOfColumns() {
160         return mNumColumns;
161     }
162 
163     /**
164      * Enable or disable child shadow.
165      * This is not only for enable/disable default shadow implementation but also subclass must
166      * respect this flag.
167      */
setShadowEnabled(boolean enabled)168     public final void setShadowEnabled(boolean enabled) {
169         mShadowEnabled = enabled;
170     }
171 
172     /**
173      * Returns true if child shadow is enabled.
174      * This is not only for enable/disable default shadow implementation but also subclass must
175      * respect this flag.
176      */
getShadowEnabled()177     public final boolean getShadowEnabled() {
178         return mShadowEnabled;
179     }
180 
181     /**
182      * Default implementation returns true if SDK version >= 21, shadow (either static or z-order
183      * based) will be applied to each individual child of {@link VerticalGridView}.
184      * Subclass may return false to disable default implementation of shadow and provide its own.
185      */
isUsingDefaultShadow()186     public boolean isUsingDefaultShadow() {
187         return ShadowOverlayHelper.supportsShadow();
188     }
189 
190     /**
191      * Enables or disabled rounded corners on children of this row.
192      * Supported on Android SDK >= L.
193      */
enableChildRoundedCorners(boolean enable)194     public final void enableChildRoundedCorners(boolean enable) {
195         mRoundedCornersEnabled = enable;
196     }
197 
198     /**
199      * Returns true if rounded corners are enabled for children of this row.
200      */
areChildRoundedCornersEnabled()201     public final boolean areChildRoundedCornersEnabled() {
202         return mRoundedCornersEnabled;
203     }
204 
205     /**
206      * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
207      * on each child of vertical grid.   If subclass returns false in isUsingDefaultShadow()
208      * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
209      */
isUsingZOrder(Context context)210     public boolean isUsingZOrder(Context context) {
211         return !Settings.getInstance(context).preferStaticShadows();
212     }
213 
needsDefaultShadow()214     final boolean needsDefaultShadow() {
215         return isUsingDefaultShadow() && getShadowEnabled();
216     }
217 
218     /**
219      * Returns the zoom factor used for focus highlighting.
220      */
getFocusZoomFactor()221     public final int getFocusZoomFactor() {
222         return mFocusZoomFactor;
223     }
224 
225     /**
226      * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
227      */
isFocusDimmerUsed()228     public final boolean isFocusDimmerUsed() {
229         return mUseFocusDimmer;
230     }
231 
232     @Override
onCreateViewHolder(ViewGroup parent)233     public final ViewHolder onCreateViewHolder(ViewGroup parent) {
234         ViewHolder vh = createGridViewHolder(parent);
235         vh.mInitialized = false;
236         vh.mItemBridgeAdapter = new VerticalGridItemBridgeAdapter();
237         initializeGridViewHolder(vh);
238         if (!vh.mInitialized) {
239             throw new RuntimeException("super.initializeGridViewHolder() must be called");
240         }
241         return vh;
242     }
243 
244     /**
245      * Subclass may override this to inflate a different layout.
246      */
createGridViewHolder(ViewGroup parent)247     protected ViewHolder createGridViewHolder(ViewGroup parent) {
248         View root = LayoutInflater.from(parent.getContext()).inflate(
249                 R.layout.lb_vertical_grid, parent, false);
250         return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid));
251     }
252 
253     /**
254      * Called after a {@link VerticalGridPresenter.ViewHolder} is created.
255      * Subclasses may override this method and start by calling
256      * super.initializeGridViewHolder(ViewHolder).
257      *
258      * @param vh The ViewHolder to initialize for the vertical grid.
259      */
initializeGridViewHolder(ViewHolder vh)260     protected void initializeGridViewHolder(ViewHolder vh) {
261         if (mNumColumns == -1) {
262             throw new IllegalStateException("Number of columns must be set");
263         }
264         if (DEBUG) Log.v(TAG, "mNumColumns " + mNumColumns);
265         vh.getGridView().setNumColumns(mNumColumns);
266         vh.mInitialized = true;
267 
268         Context context = vh.mGridView.getContext();
269         if (mShadowOverlayHelper == null) {
270             mShadowOverlayHelper = new ShadowOverlayHelper.Builder()
271                     .needsOverlay(mUseFocusDimmer)
272                     .needsShadow(needsDefaultShadow())
273                     .needsRoundedCorner(areChildRoundedCornersEnabled())
274                     .preferZOrder(isUsingZOrder(context))
275                     .keepForegroundDrawable(mKeepChildForeground)
276                     .options(createShadowOverlayOptions())
277                     .build(context);
278             if (mShadowOverlayHelper.needsWrapper()) {
279                 mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper(
280                         mShadowOverlayHelper);
281             }
282         }
283         vh.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper);
284         mShadowOverlayHelper.prepareParentForShadow(vh.mGridView);
285         vh.getGridView().setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
286                 != ShadowOverlayHelper.SHADOW_DYNAMIC);
287         FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter,
288                 mFocusZoomFactor, mUseFocusDimmer);
289 
290         final ViewHolder gridViewHolder = vh;
291         vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
292             @Override
293             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
294                 selectChildView(gridViewHolder, view);
295             }
296         });
297     }
298 
299     /**
300      * Set if keeps foreground of child of this grid, the foreground will not
301      * be used for overlay color.  Default value is true.
302      *
303      * @param keep   True if keep foreground of child of this grid.
304      */
setKeepChildForeground(boolean keep)305     public final void setKeepChildForeground(boolean keep) {
306         mKeepChildForeground = keep;
307     }
308 
309     /**
310      * Returns true if keeps foreground of child of this grid, the foreground will not
311      * be used for overlay color.  Default value is true.
312      *
313      * @return   True if keeps foreground of child of this grid.
314      */
getKeepChildForeground()315     public final boolean getKeepChildForeground() {
316         return mKeepChildForeground;
317     }
318 
319     /**
320      * Create ShadowOverlayHelper Options.  Subclass may override.
321      * e.g.
322      * <code>
323      * return new ShadowOverlayHelper.Options().roundedCornerRadius(10);
324      * </code>
325      *
326      * @return   The options to be used for shadow, overlay and rounded corner.
327      */
createShadowOverlayOptions()328     protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
329         return ShadowOverlayHelper.Options.DEFAULT;
330     }
331 
332     @Override
onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)333     public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
334         if (DEBUG) Log.v(TAG, "onBindViewHolder " + item);
335         ViewHolder vh = (ViewHolder) viewHolder;
336         vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item);
337         vh.getGridView().setAdapter(vh.mItemBridgeAdapter);
338     }
339 
340     @Override
onUnbindViewHolder(Presenter.ViewHolder viewHolder)341     public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
342         if (DEBUG) Log.v(TAG, "onUnbindViewHolder");
343         ViewHolder vh = (ViewHolder) viewHolder;
344         vh.mItemBridgeAdapter.setAdapter(null);
345         vh.getGridView().setAdapter(null);
346     }
347 
348     /**
349      * Sets the item selected listener.
350      * Since this is a grid the row parameter is always null.
351      */
setOnItemViewSelectedListener(OnItemViewSelectedListener listener)352     public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
353         mOnItemViewSelectedListener = listener;
354     }
355 
356     /**
357      * Returns the item selected listener.
358      */
getOnItemViewSelectedListener()359     public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
360         return mOnItemViewSelectedListener;
361     }
362 
363     /**
364      * Sets the item clicked listener.
365      * OnItemViewClickedListener will override {@link View.OnClickListener} that
366      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
367      * So in general, developer should choose one of the listeners but not both.
368      */
setOnItemViewClickedListener(OnItemViewClickedListener listener)369     public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
370         mOnItemViewClickedListener = listener;
371     }
372 
373     /**
374      * Returns the item clicked listener.
375      */
getOnItemViewClickedListener()376     public final OnItemViewClickedListener getOnItemViewClickedListener() {
377         return mOnItemViewClickedListener;
378     }
379 
selectChildView(ViewHolder vh, View view)380     void selectChildView(ViewHolder vh, View view) {
381         if (getOnItemViewSelectedListener() != null) {
382             ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
383                     (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
384             if (ibh == null) {
385                 getOnItemViewSelectedListener().onItemSelected(null, null, null, null);
386             } else {
387                 getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null);
388             }
389         }
390     }
391 
392     /**
393      * Changes the visibility of views.  The entrance transition will be run against the views that
394      * change visibilities.  This method is called by the fragment, it should not be called
395      * directly by the application.
396      *
397      * @param holder         The ViewHolder for the vertical grid.
398      * @param afterEntrance  true if children of vertical grid participating in entrance transition
399      *                       should be set to visible, false otherwise.
400      */
setEntranceTransitionState(VerticalGridPresenter.ViewHolder holder, boolean afterEntrance)401     public void setEntranceTransitionState(VerticalGridPresenter.ViewHolder holder,
402             boolean afterEntrance) {
403         holder.mGridView.setChildrenVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
404     }
405 }
406