VerticalGridPresenter.java revision a00bada00bff4a58436a39472ab14ccb7a8f619d
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 {@link VerticalGridView}. 25 */ 26public class VerticalGridPresenter extends Presenter { 27 private static final String TAG = "GridPresenter"; 28 private static final boolean DEBUG = false; 29 30 class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter { 31 @Override 32 public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) { 33 // Only when having an OnItemClickListner, we attach the OnClickListener. 34 if (getOnItemViewClickedListener() != null) { 35 final View itemView = itemViewHolder.mHolder.view; 36 itemView.setOnClickListener(new View.OnClickListener() { 37 @Override 38 public void onClick(View view) { 39 if (getOnItemViewClickedListener() != null) { 40 // Row is always null 41 getOnItemViewClickedListener().onItemClicked( 42 itemViewHolder.mHolder, itemViewHolder.mItem, null, null); 43 } 44 } 45 }); 46 } 47 } 48 49 @Override 50 public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) { 51 if (getOnItemViewClickedListener() != null) { 52 viewHolder.mHolder.view.setOnClickListener(null); 53 } 54 } 55 56 @Override 57 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { 58 viewHolder.itemView.setActivated(true); 59 } 60 } 61 62 /** 63 * ViewHolder for the VerticalGridPresenter. 64 */ 65 public static class ViewHolder extends Presenter.ViewHolder { 66 ItemBridgeAdapter mItemBridgeAdapter; 67 final VerticalGridView mGridView; 68 boolean mInitialized; 69 70 public ViewHolder(VerticalGridView view) { 71 super(view); 72 mGridView = view; 73 } 74 75 public VerticalGridView getGridView() { 76 return mGridView; 77 } 78 } 79 80 private int mNumColumns = -1; 81 private int mFocusZoomFactor; 82 private boolean mUseFocusDimmer; 83 private boolean mShadowEnabled = true; 84 private OnItemViewSelectedListener mOnItemViewSelectedListener; 85 private OnItemViewClickedListener mOnItemViewClickedListener; 86 private boolean mRoundedCornersEnabled = true; 87 88 /** 89 * Constructs a VerticalGridPresenter with defaults. 90 * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and 91 * enabled dimming on focus. 92 */ 93 public VerticalGridPresenter() { 94 this(FocusHighlight.ZOOM_FACTOR_LARGE); 95 } 96 97 /** 98 * Constructs a VerticalGridPresenter with the given parameters. 99 * 100 * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of 101 * {@link FocusHighlight#ZOOM_FACTOR_NONE}, 102 * {@link FocusHighlight#ZOOM_FACTOR_SMALL}, 103 * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}, 104 * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}, 105 * {@link FocusHighlight#ZOOM_FACTOR_LARGE} 106 * enabled dimming on focus. 107 */ 108 public VerticalGridPresenter(int focusZoomFactor) { 109 this(focusZoomFactor, true); 110 } 111 112 /** 113 * Constructs a VerticalGridPresenter with the given parameters. 114 * 115 * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of 116 * {@link FocusHighlight#ZOOM_FACTOR_NONE}, 117 * {@link FocusHighlight#ZOOM_FACTOR_SMALL}, 118 * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}, 119 * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}, 120 * {@link FocusHighlight#ZOOM_FACTOR_LARGE} 121 * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer 122 */ 123 public VerticalGridPresenter(int focusZoomFactor, boolean useFocusDimmer) { 124 mFocusZoomFactor = focusZoomFactor; 125 mUseFocusDimmer = useFocusDimmer; 126 } 127 128 /** 129 * Sets the number of columns in the vertical grid. 130 */ 131 public void setNumberOfColumns(int numColumns) { 132 if (numColumns < 0) { 133 throw new IllegalArgumentException("Invalid number of columns"); 134 } 135 if (mNumColumns != numColumns) { 136 mNumColumns = numColumns; 137 } 138 } 139 140 /** 141 * Returns the number of columns in the vertical grid. 142 */ 143 public int getNumberOfColumns() { 144 return mNumColumns; 145 } 146 147 /** 148 * Enable or disable child shadow. 149 * This is not only for enable/disable default shadow implementation but also subclass must 150 * respect this flag. 151 */ 152 public final void setShadowEnabled(boolean enabled) { 153 mShadowEnabled = enabled; 154 } 155 156 /** 157 * Returns true if child shadow is enabled. 158 * This is not only for enable/disable default shadow implementation but also subclass must 159 * respect this flag. 160 */ 161 public final boolean getShadowEnabled() { 162 return mShadowEnabled; 163 } 164 165 /** 166 * Returns true if opticalBounds is supported (SDK >= 18) so that default shadow 167 * is applied to each individual child of {@link VerticalGridView}. 168 * Subclass may return false to disable. 169 */ 170 public boolean isUsingDefaultShadow() { 171 return ShadowOverlayContainer.supportsShadow(); 172 } 173 174 /** 175 * Enables or disabled rounded corners on children of this row. 176 * Supported on Android SDK >= L. 177 */ 178 public final void enableChildRoundedCorners(boolean enable) { 179 mRoundedCornersEnabled = enable; 180 } 181 182 /** 183 * Returns true if rounded corners are enabled for children of this row. 184 */ 185 public final boolean areChildRoundedCornersEnabled() { 186 return mRoundedCornersEnabled; 187 } 188 189 /** 190 * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled 191 * on each child of vertical grid. If subclass returns false in isUsingDefaultShadow() 192 * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. 193 */ 194 public boolean isUsingZOrder() { 195 return ShadowHelper.getInstance().usesZShadow(); 196 } 197 198 final boolean needsDefaultShadow() { 199 return isUsingDefaultShadow() && getShadowEnabled(); 200 } 201 202 /** 203 * Returns the zoom factor used for focus highlighting. 204 */ 205 public final int getFocusZoomFactor() { 206 return mFocusZoomFactor; 207 } 208 209 /** 210 * Returns true if the focus dimmer is used for focus highlighting; false otherwise. 211 */ 212 public final boolean isFocusDimmerUsed() { 213 return mUseFocusDimmer; 214 } 215 216 217 @Override 218 public final ViewHolder onCreateViewHolder(ViewGroup parent) { 219 ViewHolder vh = createGridViewHolder(parent); 220 vh.mInitialized = false; 221 vh.mItemBridgeAdapter = new VerticalGridItemBridgeAdapter(); 222 initializeGridViewHolder(vh); 223 if (!vh.mInitialized) { 224 throw new RuntimeException("super.initializeGridViewHolder() must be called"); 225 } 226 return vh; 227 } 228 229 /** 230 * Subclass may override this to inflate a different layout. 231 */ 232 protected ViewHolder createGridViewHolder(ViewGroup parent) { 233 View root = LayoutInflater.from(parent.getContext()).inflate( 234 R.layout.lb_vertical_grid, parent, false); 235 return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid)); 236 } 237 238 private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { 239 @Override 240 public View createWrapper(View root) { 241 ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext()); 242 wrapper.setLayoutParams( 243 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 244 wrapper.initialize(needsDefaultShadow(), true, areChildRoundedCornersEnabled()); 245 return wrapper; 246 } 247 @Override 248 public void wrap(View wrapper, View wrapped) { 249 ((ShadowOverlayContainer) wrapper).wrap(wrapped); 250 } 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 */ 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 vh.mItemBridgeAdapter.setWrapper(mWrapper); 269 if (needsDefaultShadow() || areChildRoundedCornersEnabled()) { 270 ShadowOverlayContainer.prepareParentForShadow(vh.getGridView()); 271 ((ViewGroup) vh.view).setClipChildren(false); 272 } 273 vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder()); 274 FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, 275 mFocusZoomFactor, mUseFocusDimmer); 276 277 final ViewHolder gridViewHolder = vh; 278 vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() { 279 @Override 280 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 281 selectChildView(gridViewHolder, view); 282 } 283 }); 284 } 285 286 @Override 287 public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 288 if (DEBUG) Log.v(TAG, "onBindViewHolder " + item); 289 ViewHolder vh = (ViewHolder) viewHolder; 290 vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item); 291 vh.getGridView().setAdapter(vh.mItemBridgeAdapter); 292 } 293 294 @Override 295 public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { 296 if (DEBUG) Log.v(TAG, "onUnbindViewHolder"); 297 ViewHolder vh = (ViewHolder) viewHolder; 298 vh.mItemBridgeAdapter.setAdapter(null); 299 vh.getGridView().setAdapter(null); 300 } 301 302 /** 303 * Sets the item selected listener. 304 * Since this is a grid the row parameter is always null. 305 */ 306 public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 307 mOnItemViewSelectedListener = listener; 308 } 309 310 /** 311 * Returns the item selected listener. 312 */ 313 public final OnItemViewSelectedListener getOnItemViewSelectedListener() { 314 return mOnItemViewSelectedListener; 315 } 316 317 /** 318 * Sets the item clicked listener. 319 * OnItemViewClickedListener will override {@link View.OnClickListener} that 320 * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. 321 * So in general, developer should choose one of the listeners but not both. 322 */ 323 public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 324 mOnItemViewClickedListener = listener; 325 } 326 327 /** 328 * Returns the item clicked listener. 329 */ 330 public final OnItemViewClickedListener getOnItemViewClickedListener() { 331 return mOnItemViewClickedListener; 332 } 333 334 private void selectChildView(ViewHolder vh, View view) { 335 if (getOnItemViewSelectedListener() != null) { 336 ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null : 337 (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view); 338 if (ibh == null) { 339 getOnItemViewSelectedListener().onItemSelected(null, null, null, null); 340 } else { 341 getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null); 342 } 343 } 344 } 345} 346