FullWidthDetailsOverviewRowPresenter.java revision bc00c3b6c1d1be08d2f462080fb7ccd551fe65d8
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.app.Activity; 17import android.content.Context; 18import android.content.res.Resources; 19import android.graphics.Bitmap; 20import android.graphics.Color; 21import android.graphics.Rect; 22import android.graphics.drawable.Drawable; 23import android.graphics.drawable.BitmapDrawable; 24import android.graphics.drawable.ColorDrawable; 25import android.os.Handler; 26import android.support.v17.leanback.R; 27import android.support.v17.leanback.widget.ListRowPresenter.ViewHolder; 28import android.support.v7.widget.RecyclerView; 29import android.util.Log; 30import android.util.TypedValue; 31import android.view.KeyEvent; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.ViewGroup.MarginLayoutParams; 36import android.widget.FrameLayout; 37import android.widget.ImageView; 38 39import java.util.Collection; 40 41/** 42 * Renders a {@link DetailsOverviewRow} to display an overview of an item. Typically this row will 43 * be the first row in a fragment such as the 44 * {@link android.support.v17.leanback.app.DetailsFragment}. The View created by the 45 * FullWidthDetailsOverviewRowPresenter is made in three parts: logo view on the left, action list view on 46 * the top and a customizable detailed description view on the right. 47 * 48 * <p>The detailed description is rendered using a {@link Presenter} passed in 49 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter)}. Typically this will be an instance of 50 * {@link AbstractDetailsDescriptionPresenter}. The application can access the detailed description 51 * ViewHolder from {@link ViewHolder#getDetailsDescriptionViewHolder()}. 52 * </p> 53 * 54 * <p>The logo view is rendered using a customizable {@link DetailsOverviewLogoPresenter} passed in 55 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter, DetailsOverviewLogoPresenter)}. The application 56 * can access the logo ViewHolder from {@link ViewHolder#getLogoViewHolder()}. 57 * </p> 58 * 59 * <p> 60 * To support activity shared element transition, call {@link #setListener(Listener)} with 61 * {@link FullWidthDetailsOverviewSharedElementHelper} during Activity's onCreate(). Application is free to 62 * create its own "shared element helper" class using the Listener for image binding. 63 * Call {@link #setParticipatingEntranceTransition(boolean)} with false 64 * </p> 65 * 66 * <p> 67 * The view has three states: {@link #STATE_HALF} {@link #STATE_FULL} and {@link #STATE_SMALL}. See 68 * {@link android.support.v17.leanback.app.DetailsFragment} where it switches states based on 69 * selected row position. 70 * </p> 71 */ 72public class FullWidthDetailsOverviewRowPresenter extends RowPresenter { 73 74 private static final String TAG = "FullWidthDetailsOverviewRowPresenter"; 75 private static final boolean DEBUG = false; 76 77 private static Rect sTmpRect = new Rect(); 78 79 /** 80 * This is the default state corresponding to layout file. The view takes full width 81 * of screen and covers bottom half of the screen. 82 */ 83 public static final int STATE_HALF = 0; 84 /** 85 * This is the state when the view covers full width and height of screen. 86 */ 87 public static final int STATE_FULL = 1; 88 /** 89 * This is the state where the view shrinks to a small banner. 90 */ 91 public static final int STATE_SMALL = 2; 92 93 /** 94 * Listeners for events on ViewHolder. 95 */ 96 public static abstract class Listener { 97 98 /** 99 * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(ViewHolder)} is called. 100 * @param vh The ViewHolder that has bound logo view. 101 */ 102 public void onBindLogo(ViewHolder vh) { 103 } 104 105 } 106 107 class ActionsItemBridgeAdapter extends ItemBridgeAdapter { 108 FullWidthDetailsOverviewRowPresenter.ViewHolder mViewHolder; 109 110 ActionsItemBridgeAdapter(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder) { 111 mViewHolder = viewHolder; 112 } 113 114 @Override 115 public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) { 116 if (mViewHolder.getOnItemViewClickedListener() != null || 117 mActionClickedListener != null) { 118 ibvh.getPresenter().setOnClickListener( 119 ibvh.getViewHolder(), new View.OnClickListener() { 120 @Override 121 public void onClick(View v) { 122 if (mViewHolder.getOnItemViewClickedListener() != null) { 123 mViewHolder.getOnItemViewClickedListener().onItemClicked( 124 ibvh.getViewHolder(), ibvh.getItem(), 125 mViewHolder, mViewHolder.getRow()); 126 } 127 if (mActionClickedListener != null) { 128 mActionClickedListener.onActionClicked((Action) ibvh.getItem()); 129 } 130 } 131 }); 132 } 133 } 134 @Override 135 public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) { 136 if (mViewHolder.getOnItemViewClickedListener() != null || 137 mActionClickedListener != null) { 138 ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null); 139 } 140 } 141 @Override 142 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { 143 // Remove first to ensure we don't add ourselves more than once. 144 viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener); 145 viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener); 146 } 147 @Override 148 public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) { 149 viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener); 150 mViewHolder.checkFirstAndLastPosition(false); 151 } 152 } 153 154 /** 155 * A ViewHolder for the DetailsOverviewRow. 156 */ 157 public class ViewHolder extends RowPresenter.ViewHolder { 158 159 protected final DetailsOverviewRow.Listener mRowListener = createRowListener(); 160 161 protected DetailsOverviewRow.Listener createRowListener() { 162 return new DetailsOverviewRowListener(); 163 } 164 165 public class DetailsOverviewRowListener extends DetailsOverviewRow.Listener { 166 @Override 167 public void onImageDrawableChanged(DetailsOverviewRow row) { 168 mHandler.removeCallbacks(mUpdateDrawableCallback); 169 mHandler.post(mUpdateDrawableCallback); 170 } 171 172 @Override 173 public void onItemChanged(DetailsOverviewRow row) { 174 if (mDetailsDescriptionViewHolder != null) { 175 mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder); 176 } 177 mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem()); 178 } 179 180 @Override 181 public void onActionsAdapterChanged(DetailsOverviewRow row) { 182 bindActions(row.getActionsAdapter()); 183 } 184 }; 185 186 final ViewGroup mOverviewRoot; 187 final FrameLayout mOverviewFrame; 188 final FrameLayout mDetailsDescriptionFrame; 189 final HorizontalGridView mActionsRow; 190 final Presenter.ViewHolder mDetailsDescriptionViewHolder; 191 final DetailsOverviewLogoPresenter.ViewHolder mDetailsLogoViewHolder; 192 int mNumItems; 193 boolean mShowMoreRight; 194 boolean mShowMoreLeft; 195 ItemBridgeAdapter mActionBridgeAdapter; 196 protected final Handler mHandler = new Handler(); 197 int mState = STATE_HALF; 198 199 final Runnable mUpdateDrawableCallback = new Runnable() { 200 @Override 201 public void run() { 202 mDetailsOverviewLogoPresenter.onBindViewHolder(mDetailsLogoViewHolder, getRow()); 203 } 204 }; 205 206 void bindActions(ObjectAdapter adapter) { 207 mActionBridgeAdapter.setAdapter(adapter); 208 mActionsRow.setAdapter(mActionBridgeAdapter); 209 mNumItems = mActionBridgeAdapter.getItemCount(); 210 211 mShowMoreRight = false; 212 mShowMoreLeft = true; 213 showMoreLeft(false); 214 } 215 216 final View.OnLayoutChangeListener mLayoutChangeListener = 217 new View.OnLayoutChangeListener() { 218 219 @Override 220 public void onLayoutChange(View v, int left, int top, int right, 221 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 222 if (DEBUG) Log.v(TAG, "onLayoutChange " + v); 223 checkFirstAndLastPosition(false); 224 } 225 }; 226 227 final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() { 228 @Override 229 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 230 dispatchItemSelection(view); 231 } 232 }; 233 234 void dispatchItemSelection(View view) { 235 if (!isSelected()) { 236 return; 237 } 238 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null ? 239 mActionsRow.getChildViewHolder(view) : 240 mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition())); 241 if (ibvh == null) { 242 if (getOnItemViewSelectedListener() != null) { 243 getOnItemViewSelectedListener().onItemSelected(null, null, 244 ViewHolder.this, getRow()); 245 } 246 } else { 247 if (getOnItemViewSelectedListener() != null) { 248 getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(), 249 ViewHolder.this, getRow()); 250 } 251 } 252 }; 253 254 final RecyclerView.OnScrollListener mScrollListener = 255 new RecyclerView.OnScrollListener() { 256 257 @Override 258 public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 259 } 260 @Override 261 public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 262 checkFirstAndLastPosition(true); 263 } 264 }; 265 266 private int getViewCenter(View view) { 267 return (view.getRight() - view.getLeft()) / 2; 268 } 269 270 private void checkFirstAndLastPosition(boolean fromScroll) { 271 RecyclerView.ViewHolder viewHolder; 272 273 viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1); 274 boolean showRight = (viewHolder == null || 275 viewHolder.itemView.getRight() > mActionsRow.getWidth()); 276 277 viewHolder = mActionsRow.findViewHolderForPosition(0); 278 boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0); 279 280 if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll + 281 " showRight " + showRight + " showLeft " + showLeft); 282 283 showMoreRight(showRight); 284 showMoreLeft(showLeft); 285 } 286 287 private void showMoreLeft(boolean show) { 288 if (show != mShowMoreLeft) { 289 mActionsRow.setFadingLeftEdge(show); 290 mShowMoreLeft = show; 291 } 292 } 293 294 private void showMoreRight(boolean show) { 295 if (show != mShowMoreRight) { 296 mActionsRow.setFadingRightEdge(show); 297 mShowMoreRight = show; 298 } 299 } 300 301 /** 302 * Constructor for the ViewHolder. 303 * 304 * @param rootView The root View that this view holder will be attached 305 * to. 306 */ 307 public ViewHolder(View rootView, Presenter detailsPresenter, 308 DetailsOverviewLogoPresenter logoPresenter) { 309 super(rootView); 310 mOverviewRoot = (ViewGroup) rootView.findViewById(R.id.details_root); 311 mOverviewFrame = (FrameLayout) rootView.findViewById(R.id.details_frame); 312 mDetailsDescriptionFrame = 313 (FrameLayout) rootView.findViewById(R.id.details_overview_description); 314 mActionsRow = 315 (HorizontalGridView) mOverviewFrame.findViewById(R.id.details_overview_actions); 316 mActionsRow.setHasOverlappingRendering(false); 317 mActionsRow.setOnScrollListener(mScrollListener); 318 mActionsRow.setAdapter(mActionBridgeAdapter); 319 mActionsRow.setOnChildSelectedListener(mChildSelectedListener); 320 321 final int fadeLength = rootView.getResources().getDimensionPixelSize( 322 R.dimen.lb_details_overview_actions_fade_size); 323 mActionsRow.setFadingRightEdgeLength(fadeLength); 324 mActionsRow.setFadingLeftEdgeLength(fadeLength); 325 mDetailsDescriptionViewHolder = 326 detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame); 327 mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view); 328 mDetailsLogoViewHolder = (DetailsOverviewLogoPresenter.ViewHolder) 329 logoPresenter.onCreateViewHolder(mOverviewRoot); 330 mOverviewRoot.addView(mDetailsLogoViewHolder.view); 331 } 332 333 /** 334 * Returns the rectangle area with a color background. 335 */ 336 public final ViewGroup getOverviewView() { 337 return mOverviewFrame; 338 } 339 340 /** 341 * Returns the ViewHolder for logo. 342 */ 343 public final DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder() { 344 return mDetailsLogoViewHolder; 345 } 346 347 /** 348 * Returns the ViewHolder for DetailsDescription. 349 */ 350 public final Presenter.ViewHolder getDetailsDescriptionViewHolder() { 351 return mDetailsDescriptionViewHolder; 352 } 353 354 /** 355 * Returns the root view for inserting details description. 356 */ 357 public final ViewGroup getDetailsDescriptionFrame() { 358 return mDetailsDescriptionFrame; 359 } 360 361 /** 362 * Returns the view of actions row. 363 */ 364 public final ViewGroup getActionsRow() { 365 return mActionsRow; 366 } 367 368 /** 369 * Returns current state of the ViewHolder set by 370 * {@link FullWidthDetailsOverviewRowPresenter#setState(ViewHolder, int)}. 371 */ 372 public final int getState() { 373 return mState; 374 } 375 } 376 377 protected int mInitialState = STATE_HALF; 378 379 private final Presenter mDetailsPresenter; 380 private final DetailsOverviewLogoPresenter mDetailsOverviewLogoPresenter; 381 private OnActionClickedListener mActionClickedListener; 382 383 private int mBackgroundColor = Color.TRANSPARENT; 384 private boolean mBackgroundColorSet; 385 386 private Listener mListener; 387 private boolean mParticipatingEntranceTransition; 388 389 /** 390 * Constructor for a FullWidthDetailsOverviewRowPresenter. 391 * 392 * @param detailsPresenter The {@link Presenter} used to render the detailed 393 * description of the row. 394 */ 395 public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter) { 396 this(detailsPresenter, new DetailsOverviewLogoPresenter()); 397 } 398 399 /** 400 * Constructor for a FullWidthDetailsOverviewRowPresenter. 401 * 402 * @param detailsPresenter The {@link Presenter} used to render the detailed 403 * description of the row. 404 * @param logoPresenter The {@link Presenter} used to render the logo view. 405 */ 406 public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter, 407 DetailsOverviewLogoPresenter logoPresenter) { 408 setHeaderPresenter(null); 409 setSelectEffectEnabled(false); 410 mDetailsPresenter = detailsPresenter; 411 mDetailsOverviewLogoPresenter = logoPresenter; 412 } 413 414 /** 415 * Sets the listener for Action click events. 416 */ 417 public void setOnActionClickedListener(OnActionClickedListener listener) { 418 mActionClickedListener = listener; 419 } 420 421 /** 422 * Returns the listener for Action click events. 423 */ 424 public OnActionClickedListener getOnActionClickedListener() { 425 return mActionClickedListener; 426 } 427 428 /** 429 * Sets the background color. If not set, a default from the theme will be used. 430 */ 431 public void setBackgroundColor(int color) { 432 mBackgroundColor = color; 433 mBackgroundColorSet = true; 434 } 435 436 /** 437 * Returns the background color. If no background color was set, transparent 438 * is returned. 439 */ 440 public int getBackgroundColor() { 441 return mBackgroundColor; 442 } 443 444 /** 445 * Returns true if the overview should be part of shared element transition. 446 */ 447 public final boolean isParticipatingEntranceTransition() { 448 return mParticipatingEntranceTransition; 449 } 450 451 /** 452 * Sets if the overview should be part of shared element transition. 453 */ 454 public final void setParticipatingEntranceTransition(boolean participating) { 455 mParticipatingEntranceTransition = participating; 456 } 457 458 /** 459 * Change the initial state used to create ViewHolder. 460 */ 461 public final void setInitialState(int state) { 462 mInitialState = state; 463 } 464 465 /** 466 * Returns the initial state used to create ViewHolder. 467 */ 468 public final int getInitialState() { 469 return mInitialState; 470 } 471 472 @Override 473 protected boolean isClippingChildren() { 474 return true; 475 } 476 477 /** 478 * Set listener for details overview presenter. Must be called before creating 479 * ViewHolder. 480 */ 481 public final void setListener(Listener listener) { 482 mListener = listener; 483 } 484 485 private int getDefaultBackgroundColor(Context context) { 486 TypedValue outValue = new TypedValue(); 487 if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) { 488 return context.getResources().getColor(outValue.resourceId); 489 } 490 return context.getResources().getColor(R.color.lb_default_brand_color); 491 } 492 493 /** 494 * Get resource id to inflate the layout. The layout must match {@link #STATE_HALF} 495 */ 496 protected int getLayoutResourceId() { 497 return R.layout.lb_fullwidth_details_overview; 498 } 499 500 @Override 501 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 502 View v = LayoutInflater.from(parent.getContext()) 503 .inflate(getLayoutResourceId(), parent, false); 504 final ViewHolder vh = new ViewHolder(v, mDetailsPresenter, mDetailsOverviewLogoPresenter); 505 mDetailsOverviewLogoPresenter.setContext(vh.mDetailsLogoViewHolder, vh, this); 506 setState(vh, mInitialState); 507 508 vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh); 509 final View overview = vh.mOverviewFrame; 510 final int bgColor = mBackgroundColorSet ? mBackgroundColor : 511 getDefaultBackgroundColor(overview.getContext()); 512 overview.setBackgroundColor(bgColor); 513 RoundedRectHelper.getInstance().setClipToRoundedOutline(overview, true); 514 515 if (!getSelectEffectEnabled()) { 516 vh.mOverviewFrame.setForeground(null); 517 } 518 519 vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() { 520 @Override 521 public boolean onUnhandledKey(KeyEvent event) { 522 if (vh.getOnKeyListener() != null) { 523 if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) { 524 return true; 525 } 526 } 527 return false; 528 } 529 }); 530 return vh; 531 } 532 533 private static int getNonNegativeWidth(Drawable drawable) { 534 final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth(); 535 return (width > 0 ? width : 0); 536 } 537 538 private static int getNonNegativeHeight(Drawable drawable) { 539 final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight(); 540 return (height > 0 ? height : 0); 541 } 542 543 @Override 544 protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) { 545 super.onBindRowViewHolder(holder, item); 546 547 DetailsOverviewRow row = (DetailsOverviewRow) item; 548 ViewHolder vh = (ViewHolder) holder; 549 550 mDetailsOverviewLogoPresenter.onBindViewHolder(vh.mDetailsLogoViewHolder, row); 551 mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem()); 552 vh.bindActions(row.getActionsAdapter()); 553 row.addListener(vh.mRowListener); 554 } 555 556 @Override 557 protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) { 558 ViewHolder vh = (ViewHolder) holder; 559 DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow(); 560 dor.removeListener(vh.mRowListener); 561 mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder); 562 mDetailsOverviewLogoPresenter.onUnbindViewHolder(vh.mDetailsLogoViewHolder); 563 super.onUnbindRowViewHolder(holder); 564 } 565 566 @Override 567 public final boolean isUsingDefaultSelectEffect() { 568 return false; 569 } 570 571 @Override 572 protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) { 573 super.onSelectLevelChanged(holder); 574 if (getSelectEffectEnabled()) { 575 ViewHolder vh = (ViewHolder) holder; 576 int dimmedColor = vh.mColorDimmer.getPaint().getColor(); 577 ((ColorDrawable) vh.mOverviewFrame.getForeground().mutate()).setColor(dimmedColor); 578 } 579 } 580 581 @Override 582 protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) { 583 super.onRowViewAttachedToWindow(vh); 584 ViewHolder viewHolder = (ViewHolder) vh; 585 mDetailsPresenter.onViewAttachedToWindow(viewHolder.mDetailsDescriptionViewHolder); 586 mDetailsOverviewLogoPresenter.onViewAttachedToWindow(viewHolder.mDetailsLogoViewHolder); 587 } 588 589 @Override 590 protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) { 591 super.onRowViewDetachedFromWindow(vh); 592 ViewHolder viewHolder = (ViewHolder) vh; 593 mDetailsPresenter.onViewDetachedFromWindow(viewHolder.mDetailsDescriptionViewHolder); 594 mDetailsOverviewLogoPresenter.onViewDetachedFromWindow(viewHolder.mDetailsLogoViewHolder); 595 } 596 597 /** 598 * Called by {@link DetailsOverviewLogoPresenter} to notify logo was bound to view. 599 * Application should not directly call this method. 600 * @param viewHolder The row ViewHolder that has logo bound to view. 601 */ 602 public final void notifyOnBindLogo(ViewHolder viewHolder) { 603 onLayoutOverviewFrame(viewHolder, viewHolder.getState(), true); 604 onLayoutLogo(viewHolder, viewHolder.getState(), true); 605 if (mListener != null) { 606 mListener.onBindLogo(viewHolder); 607 } 608 } 609 610 /** 611 * Layout logo position based on current state. Subclass may override. 612 * The method is called when a logo is bound to view or state changes. 613 * @param viewHolder The row ViewHolder that contains the logo. 614 * @param oldState The old state, can be same as current viewHolder.getState() 615 * @param logoChanged Whether logo was changed. 616 */ 617 protected void onLayoutLogo(ViewHolder viewHolder, int oldState, boolean logoChanged) { 618 View v = viewHolder.getLogoViewHolder().view; 619 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) 620 v.getLayoutParams(); 621 lp.setMarginStart(v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_left) 622 - lp.width); 623 switch (viewHolder.getState()) { 624 case STATE_FULL: 625 default: 626 lp.topMargin = 627 v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_blank_height) 628 - lp.height / 2; 629 break; 630 case STATE_HALF: 631 lp.topMargin = v.getResources().getDimensionPixelSize( 632 R.dimen.lb_details_v2_blank_height) + v.getResources() 633 .getDimensionPixelSize(R.dimen.lb_details_v2_actions_height) + v 634 .getResources().getDimensionPixelSize( 635 R.dimen.lb_details_v2_description_margin_top); 636 break; 637 case STATE_SMALL: 638 lp.topMargin = 0; 639 break; 640 } 641 v.setLayoutParams(lp); 642 } 643 644 /** 645 * Layout overview frame based on current state. Subclass may override. 646 * The method is called when a logo is bound to view or state changes. 647 * @param viewHolder The row ViewHolder that contains the logo. 648 * @param oldState The old state, can be same as current viewHolder.getState() 649 * @param logoChanged Whether logo was changed. 650 */ 651 protected void onLayoutOverviewFrame(ViewHolder viewHolder, int oldState, boolean logoChanged) { 652 boolean wasBanner = oldState == STATE_SMALL; 653 boolean isBanner = viewHolder.getState() == STATE_SMALL; 654 if (wasBanner != isBanner || logoChanged) { 655 Resources res = viewHolder.view.getResources(); 656 MarginLayoutParams lpFrame = 657 (MarginLayoutParams) viewHolder.getOverviewView().getLayoutParams(); 658 int framePaddingStart; 659 if (isBanner) { 660 lpFrame.topMargin = 0; 661 if (mDetailsOverviewLogoPresenter.isBoundToImage(viewHolder.getLogoViewHolder(), 662 (DetailsOverviewRow) viewHolder.getRow())) { 663 View logoView = viewHolder.getLogoViewHolder().view; 664 ViewGroup.MarginLayoutParams lpLogo = 665 (ViewGroup.MarginLayoutParams) logoView.getLayoutParams(); 666 framePaddingStart = lpLogo.width; 667 } else { 668 framePaddingStart = 0; 669 } 670 lpFrame.leftMargin = lpFrame.rightMargin = 671 res.getDimensionPixelSize(R.dimen.lb_details_v2_left) - framePaddingStart; 672 } else { 673 lpFrame.topMargin = res.getDimensionPixelSize(R.dimen.lb_details_v2_blank_height); 674 framePaddingStart = res.getDimensionPixelSize(R.dimen.lb_details_v2_left); 675 lpFrame.leftMargin = lpFrame.rightMargin = 0; 676 } 677 viewHolder.getOverviewView().setLayoutParams(lpFrame); 678 viewHolder.getOverviewView().setPaddingRelative(framePaddingStart, 679 viewHolder.getOverviewView().getPaddingTop(), 680 viewHolder.getOverviewView().getPaddingEnd(), 681 viewHolder.getOverviewView().getPaddingBottom()); 682 ViewGroup.LayoutParams lpActions = viewHolder.getActionsRow().getLayoutParams(); 683 lpActions.height = 684 isBanner ? 0 : res.getDimensionPixelSize(R.dimen.lb_details_v2_actions_height); 685 viewHolder.getActionsRow().setLayoutParams(lpActions); 686 } 687 } 688 689 /** 690 * Switch state of a ViewHolder. 691 * @param viewHolder The ViewHolder to change state. 692 * @param state New state, can be {@link #STATE_FULL}, {@link #STATE_HALF} 693 * or {@link #STATE_SMALL}. 694 */ 695 public final void setState(ViewHolder viewHolder, int state) { 696 if (viewHolder.getState() != state) { 697 int oldState = viewHolder.getState(); 698 viewHolder.mState = state; 699 onStateChanged(viewHolder, oldState); 700 } 701 } 702 703 /** 704 * Called when {@link ViewHolder#getState()} changes. Subclass may override. 705 * The default implementation calls {@link #onLayoutLogo(ViewHolder, int, boolean)} and 706 * {@link #onLayoutOverviewFrame(ViewHolder, int, boolean)}. 707 * @param viewHolder The ViewHolder which state changed. 708 * @param oldState The old state. 709 */ 710 protected void onStateChanged(ViewHolder viewHolder, int oldState) { 711 onLayoutOverviewFrame(viewHolder, oldState, false); 712 onLayoutLogo(viewHolder, oldState, false); 713 } 714 715 @Override 716 public void setEntranceTransitionState(RowPresenter.ViewHolder holder, 717 boolean afterEntrance) { 718 super.setEntranceTransitionState(holder, afterEntrance); 719 if (mParticipatingEntranceTransition) { 720 holder.view.setVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE); 721 } 722 } 723} 724