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