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