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