ListRowPresenter.java revision 60f531e3d9667db9afe091f5a7979410eb7a0b48
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.content.res.TypedArray; 18import android.os.Build; 19import android.support.v17.leanback.R; 20import android.support.v17.leanback.system.Settings; 21import android.support.v17.leanback.transition.TransitionHelper; 22import android.util.Log; 23import android.view.KeyEvent; 24import android.view.View; 25import android.view.ViewGroup; 26import android.view.ViewGroup.LayoutParams; 27 28import java.util.HashMap; 29 30/** 31 * ListRowPresenter renders {@link ListRow} using a 32 * {@link HorizontalGridView} hosted in a {@link ListRowView}. 33 * 34 * <h3>Hover card</h3> 35 * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to 36 * display a view for the currently focused list item below the rendered 37 * list. This view is known as a hover card. 38 * 39 * <h3>Selection animation</h3> 40 * ListRowPresenter disables {@link RowPresenter}'s default dimming effect and draws 41 * a dim overlay on each view individually. A subclass may override and disable 42 * {@link #isUsingDefaultListSelectEffect()} and write its own dim effect in 43 * {@link #onSelectLevelChanged(RowPresenter.ViewHolder)}. 44 * 45 * <h3>Shadow</h3> 46 * ListRowPresenter applies a default shadow to each child view. Call 47 * {@link #setShadowEnabled(boolean)} to disable shadows. A subclass may override and return 48 * false in {@link #isUsingDefaultShadow()} and replace with its own shadow implementation. 49 */ 50public class ListRowPresenter extends RowPresenter { 51 52 private static final String TAG = "ListRowPresenter"; 53 private static final boolean DEBUG = false; 54 55 private static final int DEFAULT_RECYCLED_POOL_SIZE = 24; 56 57 /** 58 * ViewHolder for the ListRowPresenter. 59 */ 60 public static class ViewHolder extends RowPresenter.ViewHolder { 61 final ListRowPresenter mListRowPresenter; 62 final HorizontalGridView mGridView; 63 ItemBridgeAdapter mItemBridgeAdapter; 64 final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher(); 65 final int mPaddingTop; 66 final int mPaddingBottom; 67 final int mPaddingLeft; 68 final int mPaddingRight; 69 70 public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) { 71 super(rootView); 72 mGridView = gridView; 73 mListRowPresenter = p; 74 mPaddingTop = mGridView.getPaddingTop(); 75 mPaddingBottom = mGridView.getPaddingBottom(); 76 mPaddingLeft = mGridView.getPaddingLeft(); 77 mPaddingRight = mGridView.getPaddingRight(); 78 } 79 80 /** 81 * Gets ListRowPresenter that creates this ViewHolder. 82 * @return ListRowPresenter that creates this ViewHolder. 83 */ 84 public final ListRowPresenter getListRowPresenter() { 85 return mListRowPresenter; 86 } 87 88 /** 89 * Gets HorizontalGridView that shows a list of items. 90 * @return HorizontalGridView that shows a list of items. 91 */ 92 public final HorizontalGridView getGridView() { 93 return mGridView; 94 } 95 96 /** 97 * Gets ItemBridgeAdapter that creates the list of items. 98 * @return ItemBridgeAdapter that creates the list of items. 99 */ 100 public final ItemBridgeAdapter getBridgeAdapter() { 101 return mItemBridgeAdapter; 102 } 103 104 /** 105 * Gets selected item position in adapter. 106 * @return Selected item position in adapter. 107 */ 108 public int getSelectedPosition() { 109 return mGridView.getSelectedPosition(); 110 } 111 112 /** 113 * Gets ViewHolder at a position in adapter. Returns null if the item does not exist 114 * or the item is not bound to a view. 115 * @param position Position of the item in adapter. 116 * @return ViewHolder bounds to the item. 117 */ 118 public Presenter.ViewHolder getItemViewHolder(int position) { 119 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView 120 .findViewHolderForAdapterPosition(position); 121 if (ibvh == null) { 122 return null; 123 } 124 return ibvh.getViewHolder(); 125 } 126 } 127 128 class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter { 129 ListRowPresenter.ViewHolder mRowViewHolder; 130 131 ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) { 132 mRowViewHolder = rowViewHolder; 133 } 134 135 @Override 136 protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { 137 if (viewHolder.itemView instanceof ViewGroup) { 138 TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true); 139 } 140 if (mShadowOverlayHelper != null) { 141 mShadowOverlayHelper.onViewCreated(viewHolder.itemView); 142 } 143 } 144 145 @Override 146 public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) { 147 // Only when having an OnItemClickListner, we will attach the OnClickListener. 148 if (mRowViewHolder.getOnItemViewClickedListener() != null) { 149 viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() { 150 @Override 151 public void onClick(View v) { 152 ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) 153 mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView); 154 if (mRowViewHolder.getOnItemViewClickedListener() != null) { 155 mRowViewHolder.getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder, 156 ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow); 157 } 158 } 159 }); 160 } 161 } 162 163 @Override 164 public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) { 165 if (mRowViewHolder.getOnItemViewClickedListener() != null) { 166 viewHolder.mHolder.view.setOnClickListener(null); 167 } 168 } 169 170 @Override 171 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { 172 if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) { 173 int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor(); 174 mShadowOverlayHelper.setOverlayColor(viewHolder.itemView, dimmedColor); 175 } 176 mRowViewHolder.syncActivatedStatus(viewHolder.itemView); 177 } 178 179 @Override 180 public void onAddPresenter(Presenter presenter, int type) { 181 mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews( 182 type, getRecycledPoolSize(presenter)); 183 } 184 } 185 186 private int mRowHeight; 187 private int mExpandedRowHeight; 188 private PresenterSelector mHoverCardPresenterSelector; 189 private int mFocusZoomFactor; 190 private boolean mUseFocusDimmer; 191 private boolean mShadowEnabled = true; 192 private int mBrowseRowsFadingEdgeLength = -1; 193 private boolean mRoundedCornersEnabled = true; 194 private boolean mKeepChildForeground = true; 195 private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>(); 196 private ShadowOverlayHelper mShadowOverlayHelper; 197 private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper; 198 199 private static int sSelectedRowTopPadding; 200 private static int sExpandedSelectedRowTopPadding; 201 private static int sExpandedRowNoHovercardBottomPadding; 202 203 /** 204 * Constructs a ListRowPresenter with defaults. 205 * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and 206 * disabled dimming on focus. 207 */ 208 public ListRowPresenter() { 209 this(FocusHighlight.ZOOM_FACTOR_MEDIUM); 210 } 211 212 /** 213 * Constructs a ListRowPresenter with the given parameters. 214 * 215 * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of 216 * {@link FocusHighlight#ZOOM_FACTOR_NONE}, 217 * {@link FocusHighlight#ZOOM_FACTOR_SMALL}, 218 * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}, 219 * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}, 220 * {@link FocusHighlight#ZOOM_FACTOR_LARGE} 221 * Dimming on focus defaults to disabled. 222 */ 223 public ListRowPresenter(int focusZoomFactor) { 224 this(focusZoomFactor, false); 225 } 226 227 /** 228 * Constructs a ListRowPresenter with the given parameters. 229 * 230 * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of 231 * {@link FocusHighlight#ZOOM_FACTOR_NONE}, 232 * {@link FocusHighlight#ZOOM_FACTOR_SMALL}, 233 * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}, 234 * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}, 235 * {@link FocusHighlight#ZOOM_FACTOR_LARGE} 236 * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer 237 */ 238 public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) { 239 if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) { 240 throw new IllegalArgumentException("Unhandled zoom factor"); 241 } 242 mFocusZoomFactor = focusZoomFactor; 243 mUseFocusDimmer = useFocusDimmer; 244 } 245 246 /** 247 * Sets the row height for rows created by this Presenter. Rows 248 * created before calling this method will not be updated. 249 * 250 * @param rowHeight Row height in pixels, or WRAP_CONTENT, or 0 251 * to use the default height. 252 */ 253 public void setRowHeight(int rowHeight) { 254 mRowHeight = rowHeight; 255 } 256 257 /** 258 * Returns the row height for list rows created by this Presenter. 259 */ 260 public int getRowHeight() { 261 return mRowHeight; 262 } 263 264 /** 265 * Sets the expanded row height for rows created by this Presenter. 266 * If not set, expanded rows have the same height as unexpanded 267 * rows. 268 * 269 * @param rowHeight The row height in to use when the row is expanded, 270 * in pixels, or WRAP_CONTENT, or 0 to use the default. 271 */ 272 public void setExpandedRowHeight(int rowHeight) { 273 mExpandedRowHeight = rowHeight; 274 } 275 276 /** 277 * Returns the expanded row height for rows created by this Presenter. 278 */ 279 public int getExpandedRowHeight() { 280 return mExpandedRowHeight != 0 ? mExpandedRowHeight : mRowHeight; 281 } 282 283 /** 284 * Returns the zoom factor used for focus highlighting. 285 */ 286 public final int getFocusZoomFactor() { 287 return mFocusZoomFactor; 288 } 289 290 /** 291 * Returns the zoom factor used for focus highlighting. 292 * @deprecated use {@link #getFocusZoomFactor} instead. 293 */ 294 @Deprecated 295 public final int getZoomFactor() { 296 return mFocusZoomFactor; 297 } 298 299 /** 300 * Returns true if the focus dimmer is used for focus highlighting; false otherwise. 301 */ 302 public final boolean isFocusDimmerUsed() { 303 return mUseFocusDimmer; 304 } 305 306 @Override 307 protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) { 308 super.initializeRowViewHolder(holder); 309 final ViewHolder rowViewHolder = (ViewHolder) holder; 310 Context context = holder.view.getContext(); 311 if (mShadowOverlayHelper == null) { 312 mShadowOverlayHelper = new ShadowOverlayHelper.Builder() 313 .needsOverlay(needsDefaultListSelectEffect()) 314 .needsShadow(needsDefaultShadow()) 315 .needsRoundedCorner(areChildRoundedCornersEnabled()) 316 .preferZOrder(isUsingZOrder(context)) 317 .keepForegroundDrawable(mKeepChildForeground) 318 .options(createShadowOverlayOptions()) 319 .build(context); 320 if (mShadowOverlayHelper.needsWrapper()) { 321 mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper( 322 mShadowOverlayHelper); 323 } 324 } 325 rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder); 326 // set wrapper if needed 327 rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper); 328 mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView); 329 330 FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter, 331 mFocusZoomFactor, mUseFocusDimmer); 332 rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType() 333 == ShadowOverlayHelper.SHADOW_STATIC); 334 rowViewHolder.mGridView.setOnChildSelectedListener( 335 new OnChildSelectedListener() { 336 @Override 337 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 338 selectChildView(rowViewHolder, view, true); 339 } 340 }); 341 rowViewHolder.mGridView.setOnUnhandledKeyListener( 342 new BaseGridView.OnUnhandledKeyListener() { 343 @Override 344 public boolean onUnhandledKey(KeyEvent event) { 345 if (rowViewHolder.getOnKeyListener() != null && 346 rowViewHolder.getOnKeyListener().onKey( 347 rowViewHolder.view, event.getKeyCode(), event)) { 348 return true; 349 } 350 return false; 351 } 352 }); 353 } 354 355 final boolean needsDefaultListSelectEffect() { 356 return isUsingDefaultListSelectEffect() && getSelectEffectEnabled(); 357 } 358 359 /** 360 * Sets the recycled pool size for the given presenter. 361 */ 362 public void setRecycledPoolSize(Presenter presenter, int size) { 363 mRecycledPoolSize.put(presenter, size); 364 } 365 366 /** 367 * Returns the recycled pool size for the given presenter. 368 */ 369 public int getRecycledPoolSize(Presenter presenter) { 370 return mRecycledPoolSize.containsKey(presenter) ? mRecycledPoolSize.get(presenter) : 371 DEFAULT_RECYCLED_POOL_SIZE; 372 } 373 374 /** 375 * Sets the {@link PresenterSelector} used for showing a select object in a hover card. 376 */ 377 public final void setHoverCardPresenterSelector(PresenterSelector selector) { 378 mHoverCardPresenterSelector = selector; 379 } 380 381 /** 382 * Returns the {@link PresenterSelector} used for showing a select object in a hover card. 383 */ 384 public final PresenterSelector getHoverCardPresenterSelector() { 385 return mHoverCardPresenterSelector; 386 } 387 388 /* 389 * Perform operations when a child of horizontal grid view is selected. 390 */ 391 private void selectChildView(ViewHolder rowViewHolder, View view, boolean fireEvent) { 392 if (view != null) { 393 if (rowViewHolder.mExpanded && rowViewHolder.mSelected) { 394 ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) 395 rowViewHolder.mGridView.getChildViewHolder(view); 396 397 if (mHoverCardPresenterSelector != null) { 398 rowViewHolder.mHoverCardViewSwitcher.select( 399 rowViewHolder.mGridView, view, ibh.mItem); 400 } 401 if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) { 402 rowViewHolder.getOnItemViewSelectedListener().onItemSelected( 403 ibh.mHolder, ibh.mItem, rowViewHolder, rowViewHolder.mRow); 404 } 405 } 406 } else { 407 if (mHoverCardPresenterSelector != null) { 408 rowViewHolder.mHoverCardViewSwitcher.unselect(); 409 } 410 if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) { 411 rowViewHolder.getOnItemViewSelectedListener().onItemSelected( 412 null, null, rowViewHolder, rowViewHolder.mRow); 413 } 414 } 415 } 416 417 private static void initStatics(Context context) { 418 if (sSelectedRowTopPadding == 0) { 419 sSelectedRowTopPadding = context.getResources().getDimensionPixelSize( 420 R.dimen.lb_browse_selected_row_top_padding); 421 sExpandedSelectedRowTopPadding = context.getResources().getDimensionPixelSize( 422 R.dimen.lb_browse_expanded_selected_row_top_padding); 423 sExpandedRowNoHovercardBottomPadding = context.getResources().getDimensionPixelSize( 424 R.dimen.lb_browse_expanded_row_no_hovercard_bottom_padding); 425 } 426 } 427 428 private int getSpaceUnderBaseline(ListRowPresenter.ViewHolder vh) { 429 RowHeaderPresenter.ViewHolder headerViewHolder = vh.getHeaderViewHolder(); 430 if (headerViewHolder != null) { 431 if (getHeaderPresenter() != null) { 432 return getHeaderPresenter().getSpaceUnderBaseline(headerViewHolder); 433 } 434 return headerViewHolder.view.getPaddingBottom(); 435 } 436 return 0; 437 } 438 439 private void setVerticalPadding(ListRowPresenter.ViewHolder vh) { 440 int paddingTop, paddingBottom; 441 // Note: sufficient bottom padding needed for card shadows. 442 if (vh.isExpanded()) { 443 int headerSpaceUnderBaseline = getSpaceUnderBaseline(vh); 444 if (DEBUG) Log.v(TAG, "headerSpaceUnderBaseline " + headerSpaceUnderBaseline); 445 paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop) - 446 headerSpaceUnderBaseline; 447 paddingBottom = mHoverCardPresenterSelector == null ? 448 sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom; 449 } else if (vh.isSelected()) { 450 paddingTop = sSelectedRowTopPadding - vh.mPaddingBottom; 451 paddingBottom = sSelectedRowTopPadding; 452 } else { 453 paddingTop = 0; 454 paddingBottom = vh.mPaddingBottom; 455 } 456 vh.getGridView().setPadding(vh.mPaddingLeft, paddingTop, vh.mPaddingRight, 457 paddingBottom); 458 } 459 460 @Override 461 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 462 initStatics(parent.getContext()); 463 ListRowView rowView = new ListRowView(parent.getContext()); 464 setupFadingEffect(rowView); 465 if (mRowHeight != 0) { 466 rowView.getGridView().setRowHeight(mRowHeight); 467 } 468 return new ViewHolder(rowView, rowView.getGridView(), this); 469 } 470 471 /** 472 * Dispatch item selected event using current selected item in the {@link HorizontalGridView}. 473 * The method should only be called from onRowViewSelected(). 474 */ 475 @Override 476 protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) { 477 ViewHolder vh = (ViewHolder)holder; 478 ItemBridgeAdapter.ViewHolder itemViewHolder = (ItemBridgeAdapter.ViewHolder) 479 vh.mGridView.findViewHolderForPosition(vh.mGridView.getSelectedPosition()); 480 if (itemViewHolder == null) { 481 super.dispatchItemSelectedListener(holder, selected); 482 return; 483 } 484 485 if (selected) { 486 if (holder.getOnItemViewSelectedListener() != null) { 487 holder.getOnItemViewSelectedListener().onItemSelected( 488 itemViewHolder.getViewHolder(), itemViewHolder.mItem, vh, vh.getRow()); 489 } 490 } 491 } 492 493 @Override 494 protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) { 495 super.onRowViewSelected(holder, selected); 496 ViewHolder vh = (ViewHolder) holder; 497 setVerticalPadding(vh); 498 updateFooterViewSwitcher(vh); 499 } 500 501 /* 502 * Show or hide hover card when row selection or expanded state is changed. 503 */ 504 private void updateFooterViewSwitcher(ViewHolder vh) { 505 if (vh.mExpanded && vh.mSelected) { 506 if (mHoverCardPresenterSelector != null) { 507 vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view, 508 mHoverCardPresenterSelector); 509 } 510 ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder) 511 vh.mGridView.findViewHolderForPosition( 512 vh.mGridView.getSelectedPosition()); 513 selectChildView(vh, ibh == null ? null : ibh.itemView, false); 514 } else { 515 if (mHoverCardPresenterSelector != null) { 516 vh.mHoverCardViewSwitcher.unselect(); 517 } 518 } 519 } 520 521 private void setupFadingEffect(ListRowView rowView) { 522 // content is completely faded at 1/2 padding of left, fading length is 1/2 of padding. 523 HorizontalGridView gridView = rowView.getGridView(); 524 if (mBrowseRowsFadingEdgeLength < 0) { 525 TypedArray ta = gridView.getContext() 526 .obtainStyledAttributes(R.styleable.LeanbackTheme); 527 mBrowseRowsFadingEdgeLength = (int) ta.getDimension( 528 R.styleable.LeanbackTheme_browseRowsFadingEdgeLength, 0); 529 ta.recycle(); 530 } 531 gridView.setFadingLeftEdgeLength(mBrowseRowsFadingEdgeLength); 532 } 533 534 @Override 535 protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) { 536 super.onRowViewExpanded(holder, expanded); 537 ViewHolder vh = (ViewHolder) holder; 538 if (getRowHeight() != getExpandedRowHeight()) { 539 int newHeight = expanded ? getExpandedRowHeight() : getRowHeight(); 540 vh.getGridView().setRowHeight(newHeight); 541 } 542 setVerticalPadding(vh); 543 updateFooterViewSwitcher(vh); 544 } 545 546 @Override 547 protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) { 548 super.onBindRowViewHolder(holder, item); 549 ViewHolder vh = (ViewHolder) holder; 550 ListRow rowItem = (ListRow) item; 551 vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter()); 552 vh.mGridView.setAdapter(vh.mItemBridgeAdapter); 553 } 554 555 @Override 556 protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) { 557 ViewHolder vh = (ViewHolder) holder; 558 vh.mGridView.setAdapter(null); 559 vh.mItemBridgeAdapter.clear(); 560 super.onUnbindRowViewHolder(holder); 561 } 562 563 /** 564 * ListRowPresenter overrides the default select effect of {@link RowPresenter} 565 * and return false. 566 */ 567 @Override 568 public final boolean isUsingDefaultSelectEffect() { 569 return false; 570 } 571 572 /** 573 * Returns true so that default select effect is applied to each individual 574 * child of {@link HorizontalGridView}. Subclass may return false to disable 575 * the default implementation. 576 * @see #onSelectLevelChanged(RowPresenter.ViewHolder) 577 */ 578 public boolean isUsingDefaultListSelectEffect() { 579 return true; 580 } 581 582 /** 583 * Returns true if SDK >= 18, where default shadow 584 * is applied to each individual child of {@link HorizontalGridView}. 585 * Subclass may return false to disable. 586 */ 587 public boolean isUsingDefaultShadow() { 588 return ShadowOverlayHelper.supportsShadow(); 589 } 590 591 /** 592 * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled 593 * on each child of horizontal list. If subclass returns false in isUsingDefaultShadow() 594 * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. 595 */ 596 public boolean isUsingZOrder(Context context) { 597 return !Settings.getInstance(context).preferStaticShadows(); 598 } 599 600 /** 601 * Enables or disables child shadow. 602 * This is not only for enable/disable default shadow implementation but also subclass must 603 * respect this flag. 604 */ 605 public final void setShadowEnabled(boolean enabled) { 606 mShadowEnabled = enabled; 607 } 608 609 /** 610 * Returns true if child shadow is enabled. 611 * This is not only for enable/disable default shadow implementation but also subclass must 612 * respect this flag. 613 */ 614 public final boolean getShadowEnabled() { 615 return mShadowEnabled; 616 } 617 618 /** 619 * Enables or disabled rounded corners on children of this row. 620 * Supported on Android SDK >= L. 621 */ 622 public final void enableChildRoundedCorners(boolean enable) { 623 mRoundedCornersEnabled = enable; 624 } 625 626 /** 627 * Returns true if rounded corners are enabled for children of this row. 628 */ 629 public final boolean areChildRoundedCornersEnabled() { 630 return mRoundedCornersEnabled; 631 } 632 633 final boolean needsDefaultShadow() { 634 return isUsingDefaultShadow() && getShadowEnabled(); 635 } 636 637 /** 638 * When ListRowPresenter applies overlay color on the child, it may change child's foreground 639 * Drawable. If application uses child's foreground for other purposes such as ripple effect, 640 * it needs tell ListRowPresenter to keep the child's foreground. The default value is true. 641 * 642 * @param keep true if keep foreground of child of this row, false ListRowPresenter might change 643 * the foreground of the child. 644 */ 645 public final void setKeepChildForeground(boolean keep) { 646 mKeepChildForeground = keep; 647 } 648 649 /** 650 * Returns true if keeps foreground of child of this row, false otherwise. When 651 * ListRowPresenter applies overlay color on the child, it may change child's foreground 652 * Drawable. If application uses child's foreground for other purposes such as ripple effect, 653 * it needs tell ListRowPresenter to keep the child's foreground. The default value is true. 654 * 655 * @return true if keeps foreground of child of this row, false otherwise. 656 */ 657 public final boolean isKeepChildForeground() { 658 return mKeepChildForeground; 659 } 660 661 /** 662 * Create ShadowOverlayHelper Options. Subclass may override. 663 * e.g. 664 * <code> 665 * return new ShadowOverlayHelper.Options().roundedCornerRadius(10); 666 * </code> 667 * 668 * @return The options to be used for shadow, overlay and rouded corner. 669 */ 670 protected ShadowOverlayHelper.Options createShadowOverlayOptions() { 671 return ShadowOverlayHelper.Options.DEFAULT; 672 } 673 674 /** 675 * Applies select level to header and draw a default color dim over each child 676 * of {@link HorizontalGridView}. 677 * <p> 678 * Subclass may override this method. A subclass 679 * needs to call super.onSelectLevelChanged() for applying header select level 680 * and optionally applying a default select level to each child view of 681 * {@link HorizontalGridView} if {@link #isUsingDefaultListSelectEffect()} 682 * is true. Subclass may override {@link #isUsingDefaultListSelectEffect()} to return 683 * false and deal with the individual item select level by itself. 684 * </p> 685 */ 686 @Override 687 protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) { 688 super.onSelectLevelChanged(holder); 689 if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) { 690 ViewHolder vh = (ViewHolder) holder; 691 int dimmedColor = vh.mColorDimmer.getPaint().getColor(); 692 for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) { 693 mShadowOverlayHelper.setOverlayColor(vh.mGridView.getChildAt(i), dimmedColor); 694 } 695 if (vh.mGridView.getFadingLeftEdge()) { 696 vh.mGridView.invalidate(); 697 } 698 } 699 } 700 701 @Override 702 public void freeze(RowPresenter.ViewHolder holder, boolean freeze) { 703 ViewHolder vh = (ViewHolder) holder; 704 vh.mGridView.setScrollEnabled(!freeze); 705 } 706 707 @Override 708 public void setEntranceTransitionState(RowPresenter.ViewHolder holder, 709 boolean afterEntrance) { 710 super.setEntranceTransitionState(holder, afterEntrance); 711 ((ViewHolder) holder).mGridView.setChildrenVisibility( 712 afterEntrance? View.VISIBLE : View.INVISIBLE); 713 } 714} 715