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