FullWidthDetailsOverviewRowPresenter.java revision 0246318f27a905a31df5a8af445cfe67d31dfb68
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 ViewGroup 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 = (ViewGroup) 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 vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() { 516 @Override 517 public boolean onUnhandledKey(KeyEvent event) { 518 if (vh.getOnKeyListener() != null) { 519 if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) { 520 return true; 521 } 522 } 523 return false; 524 } 525 }); 526 return vh; 527 } 528 529 private static int getNonNegativeWidth(Drawable drawable) { 530 final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth(); 531 return (width > 0 ? width : 0); 532 } 533 534 private static int getNonNegativeHeight(Drawable drawable) { 535 final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight(); 536 return (height > 0 ? height : 0); 537 } 538 539 @Override 540 protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) { 541 super.onBindRowViewHolder(holder, item); 542 543 DetailsOverviewRow row = (DetailsOverviewRow) item; 544 ViewHolder vh = (ViewHolder) holder; 545 546 mDetailsOverviewLogoPresenter.onBindViewHolder(vh.mDetailsLogoViewHolder, row); 547 mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem()); 548 vh.bindActions(row.getActionsAdapter()); 549 row.addListener(vh.mRowListener); 550 } 551 552 @Override 553 protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) { 554 ViewHolder vh = (ViewHolder) holder; 555 DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow(); 556 dor.removeListener(vh.mRowListener); 557 mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder); 558 mDetailsOverviewLogoPresenter.onUnbindViewHolder(vh.mDetailsLogoViewHolder); 559 super.onUnbindRowViewHolder(holder); 560 } 561 562 @Override 563 public final boolean isUsingDefaultSelectEffect() { 564 return false; 565 } 566 567 @Override 568 protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) { 569 super.onRowViewAttachedToWindow(vh); 570 ViewHolder viewHolder = (ViewHolder) vh; 571 mDetailsPresenter.onViewAttachedToWindow(viewHolder.mDetailsDescriptionViewHolder); 572 mDetailsOverviewLogoPresenter.onViewAttachedToWindow(viewHolder.mDetailsLogoViewHolder); 573 } 574 575 @Override 576 protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) { 577 super.onRowViewDetachedFromWindow(vh); 578 ViewHolder viewHolder = (ViewHolder) vh; 579 mDetailsPresenter.onViewDetachedFromWindow(viewHolder.mDetailsDescriptionViewHolder); 580 mDetailsOverviewLogoPresenter.onViewDetachedFromWindow(viewHolder.mDetailsLogoViewHolder); 581 } 582 583 /** 584 * Called by {@link DetailsOverviewLogoPresenter} to notify logo was bound to view. 585 * Application should not directly call this method. 586 * @param viewHolder The row ViewHolder that has logo bound to view. 587 */ 588 public final void notifyOnBindLogo(ViewHolder viewHolder) { 589 onLayoutOverviewFrame(viewHolder, viewHolder.getState(), true); 590 onLayoutLogo(viewHolder, viewHolder.getState(), true); 591 if (mListener != null) { 592 mListener.onBindLogo(viewHolder); 593 } 594 } 595 596 /** 597 * Layout logo position based on current state. Subclass may override. 598 * The method is called when a logo is bound to view or state changes. 599 * @param viewHolder The row ViewHolder that contains the logo. 600 * @param oldState The old state, can be same as current viewHolder.getState() 601 * @param logoChanged Whether logo was changed. 602 */ 603 protected void onLayoutLogo(ViewHolder viewHolder, int oldState, boolean logoChanged) { 604 View v = viewHolder.getLogoViewHolder().view; 605 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) 606 v.getLayoutParams(); 607 lp.setMarginStart(v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_left) 608 - lp.width); 609 switch (viewHolder.getState()) { 610 case STATE_FULL: 611 default: 612 lp.topMargin = 613 v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_blank_height) 614 - lp.height / 2; 615 break; 616 case STATE_HALF: 617 lp.topMargin = v.getResources().getDimensionPixelSize( 618 R.dimen.lb_details_v2_blank_height) + v.getResources() 619 .getDimensionPixelSize(R.dimen.lb_details_v2_actions_height) + v 620 .getResources().getDimensionPixelSize( 621 R.dimen.lb_details_v2_description_margin_top); 622 break; 623 case STATE_SMALL: 624 lp.topMargin = 0; 625 break; 626 } 627 v.setLayoutParams(lp); 628 } 629 630 /** 631 * Layout overview frame based on current state. Subclass may override. 632 * The method is called when a logo is bound to view or state changes. 633 * @param viewHolder The row ViewHolder that contains the logo. 634 * @param oldState The old state, can be same as current viewHolder.getState() 635 * @param logoChanged Whether logo was changed. 636 */ 637 protected void onLayoutOverviewFrame(ViewHolder viewHolder, int oldState, boolean logoChanged) { 638 boolean wasBanner = oldState == STATE_SMALL; 639 boolean isBanner = viewHolder.getState() == STATE_SMALL; 640 if (wasBanner != isBanner || logoChanged) { 641 Resources res = viewHolder.view.getResources(); 642 MarginLayoutParams lpFrame = 643 (MarginLayoutParams) viewHolder.getOverviewView().getLayoutParams(); 644 int framePaddingStart; 645 if (isBanner) { 646 lpFrame.topMargin = 0; 647 if (mDetailsOverviewLogoPresenter.isBoundToImage(viewHolder.getLogoViewHolder(), 648 (DetailsOverviewRow) viewHolder.getRow())) { 649 View logoView = viewHolder.getLogoViewHolder().view; 650 ViewGroup.MarginLayoutParams lpLogo = 651 (ViewGroup.MarginLayoutParams) logoView.getLayoutParams(); 652 framePaddingStart = lpLogo.width; 653 } else { 654 framePaddingStart = 0; 655 } 656 lpFrame.leftMargin = lpFrame.rightMargin = 657 res.getDimensionPixelSize(R.dimen.lb_details_v2_left) - framePaddingStart; 658 } else { 659 lpFrame.topMargin = res.getDimensionPixelSize(R.dimen.lb_details_v2_blank_height); 660 framePaddingStart = res.getDimensionPixelSize(R.dimen.lb_details_v2_left); 661 lpFrame.leftMargin = lpFrame.rightMargin = 0; 662 } 663 viewHolder.getOverviewView().setLayoutParams(lpFrame); 664 viewHolder.getOverviewView().setPaddingRelative(framePaddingStart, 665 viewHolder.getOverviewView().getPaddingTop(), 666 viewHolder.getOverviewView().getPaddingEnd(), 667 viewHolder.getOverviewView().getPaddingBottom()); 668 ViewGroup.LayoutParams lpActions = viewHolder.getActionsRow().getLayoutParams(); 669 lpActions.height = 670 isBanner ? 0 : res.getDimensionPixelSize(R.dimen.lb_details_v2_actions_height); 671 viewHolder.getActionsRow().setLayoutParams(lpActions); 672 } 673 } 674 675 /** 676 * Switch state of a ViewHolder. 677 * @param viewHolder The ViewHolder to change state. 678 * @param state New state, can be {@link #STATE_FULL}, {@link #STATE_HALF} 679 * or {@link #STATE_SMALL}. 680 */ 681 public final void setState(ViewHolder viewHolder, int state) { 682 if (viewHolder.getState() != state) { 683 int oldState = viewHolder.getState(); 684 viewHolder.mState = state; 685 onStateChanged(viewHolder, oldState); 686 } 687 } 688 689 /** 690 * Called when {@link ViewHolder#getState()} changes. Subclass may override. 691 * The default implementation calls {@link #onLayoutLogo(ViewHolder, int, boolean)} and 692 * {@link #onLayoutOverviewFrame(ViewHolder, int, boolean)}. 693 * @param viewHolder The ViewHolder which state changed. 694 * @param oldState The old state. 695 */ 696 protected void onStateChanged(ViewHolder viewHolder, int oldState) { 697 onLayoutOverviewFrame(viewHolder, oldState, false); 698 onLayoutLogo(viewHolder, oldState, false); 699 } 700 701 @Override 702 public void setEntranceTransitionState(RowPresenter.ViewHolder holder, 703 boolean afterEntrance) { 704 super.setEntranceTransitionState(holder, afterEntrance); 705 if (mParticipatingEntranceTransition) { 706 holder.view.setVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE); 707 } 708 } 709} 710