CollapsingToolbarLayout.java revision 0c350f7a61259f7313b44eab8190d05454e05963
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.design.widget; 18 19import android.content.Context; 20import android.content.res.ColorStateList; 21import android.content.res.TypedArray; 22import android.graphics.Canvas; 23import android.graphics.Rect; 24import android.graphics.Typeface; 25import android.graphics.drawable.ColorDrawable; 26import android.graphics.drawable.Drawable; 27import android.support.annotation.ColorInt; 28import android.support.annotation.DrawableRes; 29import android.support.annotation.IntDef; 30import android.support.annotation.IntRange; 31import android.support.annotation.NonNull; 32import android.support.annotation.Nullable; 33import android.support.annotation.StyleRes; 34import android.support.design.R; 35import android.support.v4.content.ContextCompat; 36import android.support.v4.graphics.drawable.DrawableCompat; 37import android.support.v4.view.GravityCompat; 38import android.support.v4.view.ViewCompat; 39import android.support.v4.view.WindowInsetsCompat; 40import android.support.v7.widget.Toolbar; 41import android.text.TextUtils; 42import android.util.AttributeSet; 43import android.view.Gravity; 44import android.view.View; 45import android.view.ViewGroup; 46import android.view.ViewParent; 47import android.widget.FrameLayout; 48 49import java.lang.annotation.Retention; 50import java.lang.annotation.RetentionPolicy; 51 52import static android.support.design.widget.MathUtils.constrain; 53import static android.support.design.widget.ViewUtils.objectEquals; 54 55/** 56 * CollapsingToolbarLayout is a wrapper for {@link Toolbar} which implements a collapsing app bar. 57 * It is designed to be used as a direct child of a {@link AppBarLayout}. 58 * CollapsingToolbarLayout contains the following features: 59 * 60 * <h4>Collapsing title</h4> 61 * A title which is larger when the layout is fully visible but collapses and becomes smaller as 62 * the layout is scrolled off screen. You can set the title to display via 63 * {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the 64 * {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes. 65 * 66 * <h4>Content scrim</h4> 67 * A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold. 68 * You can change this via {@link #setContentScrim(Drawable)}. 69 * 70 * <h4>Status bar scrim</h4> 71 * A scrim which is show or hidden behind the status bar when the scroll position has hit a certain 72 * threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works 73 * on {@link android.os.Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system 74 * windows. 75 * 76 * <h4>Parallax scrolling children</h4> 77 * Child views can opt to be scrolled within this layout in a parallax fashion. 78 * See {@link LayoutParams#COLLAPSE_MODE_PARALLAX} and 79 * {@link LayoutParams#setParallaxMultiplier(float)}. 80 * 81 * <h4>Pinned position children</h4> 82 * Child views can opt to be pinned in space globally. This is useful when implementing a 83 * collapsing as it allows the {@link Toolbar} to be fixed in place even though this layout is 84 * moving. See {@link LayoutParams#COLLAPSE_MODE_PIN}. 85 * 86 * <p><strong>Do not manually add views to the Toolbar at run time</strong>. 87 * We will add a 'dummy view' to the Toolbar which allows us to work out the available space 88 * for the title. This can interfere with any views which you add.</p> 89 * 90 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance 91 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance 92 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_contentScrim 93 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMargin 94 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart 95 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd 96 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom 97 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_statusBarScrim 98 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_toolbarId 99 */ 100public class CollapsingToolbarLayout extends FrameLayout { 101 102 private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600; 103 104 private boolean mRefreshToolbar = true; 105 private int mToolbarId; 106 private Toolbar mToolbar; 107 private View mToolbarDirectChild; 108 private View mDummyView; 109 private int mToolbarDrawIndex; 110 111 private int mExpandedMarginStart; 112 private int mExpandedMarginTop; 113 private int mExpandedMarginEnd; 114 private int mExpandedMarginBottom; 115 116 private final Rect mTmpRect = new Rect(); 117 private final CollapsingTextHelper mCollapsingTextHelper; 118 private boolean mCollapsingTitleEnabled; 119 private boolean mDrawCollapsingTitle; 120 121 private Drawable mContentScrim; 122 private Drawable mStatusBarScrim; 123 private int mScrimAlpha; 124 private boolean mScrimsAreShown; 125 private ValueAnimatorCompat mScrimAnimator; 126 private long mScrimAnimationDuration; 127 private int mScrimVisibleHeightTrigger = -1; 128 129 private AppBarLayout.OnOffsetChangedListener mOnOffsetChangedListener; 130 131 private int mCurrentOffset; 132 133 private WindowInsetsCompat mLastInsets; 134 135 public CollapsingToolbarLayout(Context context) { 136 this(context, null); 137 } 138 139 public CollapsingToolbarLayout(Context context, AttributeSet attrs) { 140 this(context, attrs, 0); 141 } 142 143 public CollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) { 144 super(context, attrs, defStyleAttr); 145 146 ThemeUtils.checkAppCompatTheme(context); 147 148 mCollapsingTextHelper = new CollapsingTextHelper(this); 149 mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); 150 151 TypedArray a = context.obtainStyledAttributes(attrs, 152 R.styleable.CollapsingToolbarLayout, defStyleAttr, 153 R.style.Widget_Design_CollapsingToolbar); 154 155 mCollapsingTextHelper.setExpandedTextGravity( 156 a.getInt(R.styleable.CollapsingToolbarLayout_expandedTitleGravity, 157 GravityCompat.START | Gravity.BOTTOM)); 158 mCollapsingTextHelper.setCollapsedTextGravity( 159 a.getInt(R.styleable.CollapsingToolbarLayout_collapsedTitleGravity, 160 GravityCompat.START | Gravity.CENTER_VERTICAL)); 161 162 mExpandedMarginStart = mExpandedMarginTop = mExpandedMarginEnd = mExpandedMarginBottom = 163 a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0); 164 165 if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart)) { 166 mExpandedMarginStart = a.getDimensionPixelSize( 167 R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart, 0); 168 } 169 if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd)) { 170 mExpandedMarginEnd = a.getDimensionPixelSize( 171 R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd, 0); 172 } 173 if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop)) { 174 mExpandedMarginTop = a.getDimensionPixelSize( 175 R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop, 0); 176 } 177 if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom)) { 178 mExpandedMarginBottom = a.getDimensionPixelSize( 179 R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0); 180 } 181 182 mCollapsingTitleEnabled = a.getBoolean( 183 R.styleable.CollapsingToolbarLayout_titleEnabled, true); 184 setTitle(a.getText(R.styleable.CollapsingToolbarLayout_title)); 185 186 // First load the default text appearances 187 mCollapsingTextHelper.setExpandedTextAppearance( 188 R.style.TextAppearance_Design_CollapsingToolbar_Expanded); 189 mCollapsingTextHelper.setCollapsedTextAppearance( 190 android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title); 191 192 // Now overlay any custom text appearances 193 if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance)) { 194 mCollapsingTextHelper.setExpandedTextAppearance( 195 a.getResourceId( 196 R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance, 0)); 197 } 198 if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance)) { 199 mCollapsingTextHelper.setCollapsedTextAppearance( 200 a.getResourceId( 201 R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance, 0)); 202 } 203 204 mScrimVisibleHeightTrigger = a.getDimensionPixelSize( 205 R.styleable.CollapsingToolbarLayout_scrimVisibleHeightTrigger, -1); 206 207 mScrimAnimationDuration = a.getInt( 208 R.styleable.CollapsingToolbarLayout_scrimAnimationDuration, 209 DEFAULT_SCRIM_ANIMATION_DURATION); 210 211 setContentScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_contentScrim)); 212 setStatusBarScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_statusBarScrim)); 213 214 mToolbarId = a.getResourceId(R.styleable.CollapsingToolbarLayout_toolbarId, -1); 215 216 a.recycle(); 217 218 setWillNotDraw(false); 219 220 ViewCompat.setOnApplyWindowInsetsListener(this, 221 new android.support.v4.view.OnApplyWindowInsetsListener() { 222 @Override 223 public WindowInsetsCompat onApplyWindowInsets(View v, 224 WindowInsetsCompat insets) { 225 return onWindowInsetChanged(insets); 226 } 227 }); 228 } 229 230 @Override 231 protected void onAttachedToWindow() { 232 super.onAttachedToWindow(); 233 234 // Add an OnOffsetChangedListener if possible 235 final ViewParent parent = getParent(); 236 if (parent instanceof AppBarLayout) { 237 // Copy over from the ABL whether we should fit system windows 238 ViewCompat.setFitsSystemWindows(this, ViewCompat.getFitsSystemWindows((View) parent)); 239 240 if (mOnOffsetChangedListener == null) { 241 mOnOffsetChangedListener = new OffsetUpdateListener(); 242 } 243 ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener); 244 245 // We're attached, so lets request an inset dispatch 246 ViewCompat.requestApplyInsets(this); 247 } 248 } 249 250 @Override 251 protected void onDetachedFromWindow() { 252 // Remove our OnOffsetChangedListener if possible and it exists 253 final ViewParent parent = getParent(); 254 if (mOnOffsetChangedListener != null && parent instanceof AppBarLayout) { 255 ((AppBarLayout) parent).removeOnOffsetChangedListener(mOnOffsetChangedListener); 256 } 257 258 super.onDetachedFromWindow(); 259 } 260 261 private WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) { 262 WindowInsetsCompat newInsets = null; 263 264 if (ViewCompat.getFitsSystemWindows(this)) { 265 // If we're set to fit system windows, keep the insets 266 newInsets = insets; 267 } 268 269 // If our insets have changed, keep them and invalidate the scroll ranges... 270 if (!objectEquals(mLastInsets, newInsets)) { 271 mLastInsets = newInsets; 272 requestLayout(); 273 } 274 275 // Consume the insets. This is done so that child views with fitSystemWindows=true do not 276 // get the default padding functionality from View 277 return insets.consumeSystemWindowInsets(); 278 } 279 280 @Override 281 public void draw(Canvas canvas) { 282 super.draw(canvas); 283 284 // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below. 285 // Instead, we draw it here, before our collapsing text. 286 ensureToolbar(); 287 if (mToolbar == null && mContentScrim != null && mScrimAlpha > 0) { 288 mContentScrim.mutate().setAlpha(mScrimAlpha); 289 mContentScrim.draw(canvas); 290 } 291 292 // Let the collapsing text helper draw its text 293 if (mCollapsingTitleEnabled && mDrawCollapsingTitle) { 294 mCollapsingTextHelper.draw(canvas); 295 } 296 297 // Now draw the status bar scrim 298 if (mStatusBarScrim != null && mScrimAlpha > 0) { 299 final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 300 if (topInset > 0) { 301 mStatusBarScrim.setBounds(0, -mCurrentOffset, getWidth(), 302 topInset - mCurrentOffset); 303 mStatusBarScrim.mutate().setAlpha(mScrimAlpha); 304 mStatusBarScrim.draw(canvas); 305 } 306 } 307 } 308 309 @Override 310 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 311 // This is a little weird. Our scrim needs to be behind the Toolbar (if it is present), 312 // but in front of any other children which are behind it. To do this we intercept the 313 // drawChild() call, and draw our scrim after the preceding view is drawn 314 boolean invalidate = super.drawChild(canvas, child, drawingTime); 315 316 if (mContentScrim != null && mScrimAlpha > 0 && isToolbarChildDrawnNext(child)) { 317 mContentScrim.mutate().setAlpha(mScrimAlpha); 318 mContentScrim.draw(canvas); 319 invalidate = true; 320 } 321 322 return invalidate; 323 } 324 325 @Override 326 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 327 super.onSizeChanged(w, h, oldw, oldh); 328 if (mContentScrim != null) { 329 mContentScrim.setBounds(0, 0, w, h); 330 } 331 } 332 333 private void ensureToolbar() { 334 if (!mRefreshToolbar) { 335 return; 336 } 337 338 // First clear out the current Toolbar 339 mToolbar = null; 340 mToolbarDirectChild = null; 341 342 if (mToolbarId != -1) { 343 // If we have an ID set, try and find it and it's direct parent to us 344 mToolbar = (Toolbar) findViewById(mToolbarId); 345 if (mToolbar != null) { 346 mToolbarDirectChild = findDirectChild(mToolbar); 347 } 348 } 349 350 if (mToolbar == null) { 351 // If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find 352 // one from our direct children 353 Toolbar toolbar = null; 354 for (int i = 0, count = getChildCount(); i < count; i++) { 355 final View child = getChildAt(i); 356 if (child instanceof Toolbar) { 357 toolbar = (Toolbar) child; 358 break; 359 } 360 } 361 mToolbar = toolbar; 362 } 363 364 updateDummyView(); 365 mRefreshToolbar = false; 366 } 367 368 private boolean isToolbarChildDrawnNext(View child) { 369 return mToolbarDrawIndex >= 0 && mToolbarDrawIndex == indexOfChild(child) + 1; 370 } 371 372 /** 373 * Returns the direct child of this layout, which itself is the ancestor of the 374 * given view. 375 */ 376 private View findDirectChild(final View descendant) { 377 View directChild = descendant; 378 for (ViewParent p = descendant.getParent(); p != this && p != null; p = p.getParent()) { 379 if (p instanceof View) { 380 directChild = (View) p; 381 } 382 } 383 return directChild; 384 } 385 386 private void updateDummyView() { 387 if (!mCollapsingTitleEnabled && mDummyView != null) { 388 // If we have a dummy view and we have our title disabled, remove it from its parent 389 final ViewParent parent = mDummyView.getParent(); 390 if (parent instanceof ViewGroup) { 391 ((ViewGroup) parent).removeView(mDummyView); 392 } 393 } 394 if (mCollapsingTitleEnabled && mToolbar != null) { 395 if (mDummyView == null) { 396 mDummyView = new View(getContext()); 397 } 398 if (mDummyView.getParent() == null) { 399 mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 400 } 401 } 402 } 403 404 @Override 405 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 406 ensureToolbar(); 407 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 408 } 409 410 @Override 411 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 412 super.onLayout(changed, left, top, right, bottom); 413 414 // Update our child view offset helpers 415 for (int i = 0, z = getChildCount(); i < z; i++) { 416 final View child = getChildAt(i); 417 418 if (mLastInsets != null && !ViewCompat.getFitsSystemWindows(child)) { 419 final int insetTop = mLastInsets.getSystemWindowInsetTop(); 420 if (child.getTop() < insetTop) { 421 // If the child isn't set to fit system windows but is drawing within the inset 422 // offset it down 423 ViewCompat.offsetTopAndBottom(child, insetTop); 424 } 425 } 426 427 getViewOffsetHelper(child).onViewLayout(); 428 } 429 430 // Update the collapsed bounds by getting it's transformed bounds 431 if (mCollapsingTitleEnabled && mDummyView != null) { 432 // We only draw the title if the dummy view is being displayed (Toolbar removes 433 // views if there is no space) 434 mDrawCollapsingTitle = ViewCompat.isAttachedToWindow(mDummyView) 435 && mDummyView.getVisibility() == VISIBLE; 436 437 if (mDrawCollapsingTitle) { 438 final boolean isRtl = ViewCompat.getLayoutDirection(this) 439 == ViewCompat.LAYOUT_DIRECTION_RTL; 440 441 // Update the collapsed bounds 442 final int maxOffset = getMaxOffsetForPinChild( 443 mToolbarDirectChild != null ? mToolbarDirectChild : mToolbar); 444 ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect); 445 mCollapsingTextHelper.setCollapsedBounds( 446 mTmpRect.left + (isRtl 447 ? mToolbar.getTitleMarginEnd() 448 : mToolbar.getTitleMarginStart()), 449 mTmpRect.top + maxOffset + mToolbar.getTitleMarginTop(), 450 mTmpRect.right + (isRtl 451 ? mToolbar.getTitleMarginStart() 452 : mToolbar.getTitleMarginEnd()), 453 mTmpRect.bottom + maxOffset - mToolbar.getTitleMarginBottom()); 454 455 // Update the expanded bounds 456 mCollapsingTextHelper.setExpandedBounds( 457 isRtl ? mExpandedMarginEnd : mExpandedMarginStart, 458 mExpandedMarginTop, 459 right - left - (isRtl ? mExpandedMarginStart : mExpandedMarginEnd), 460 bottom - top - mExpandedMarginBottom); 461 // Now recalculate using the new bounds 462 mCollapsingTextHelper.recalculate(); 463 } 464 } 465 466 // Finally, set our minimum height to enable proper AppBarLayout collapsing 467 if (mToolbar != null) { 468 if (mCollapsingTitleEnabled && TextUtils.isEmpty(mCollapsingTextHelper.getText())) { 469 // If we do not currently have a title, try and grab it from the Toolbar 470 mCollapsingTextHelper.setText(mToolbar.getTitle()); 471 } 472 if (mToolbarDirectChild == null || mToolbarDirectChild == this) { 473 setMinimumHeight(getHeightWithMargins(mToolbar)); 474 mToolbarDrawIndex = indexOfChild(mToolbar); 475 } else { 476 setMinimumHeight(getHeightWithMargins(mToolbarDirectChild)); 477 mToolbarDrawIndex = indexOfChild(mToolbarDirectChild); 478 } 479 } else { 480 mToolbarDrawIndex = -1; 481 } 482 483 updateScrimVisibility(); 484 } 485 486 private static int getHeightWithMargins(@NonNull final View view) { 487 final ViewGroup.LayoutParams lp = view.getLayoutParams(); 488 if (lp instanceof MarginLayoutParams) { 489 final MarginLayoutParams mlp = (MarginLayoutParams) lp; 490 return view.getHeight() + mlp.topMargin + mlp.bottomMargin; 491 } 492 return view.getHeight(); 493 } 494 495 private static ViewOffsetHelper getViewOffsetHelper(View view) { 496 ViewOffsetHelper offsetHelper = (ViewOffsetHelper) view.getTag(R.id.view_offset_helper); 497 if (offsetHelper == null) { 498 offsetHelper = new ViewOffsetHelper(view); 499 view.setTag(R.id.view_offset_helper, offsetHelper); 500 } 501 return offsetHelper; 502 } 503 504 /** 505 * Sets the title to be displayed by this view, if enabled. 506 * 507 * @see #setTitleEnabled(boolean) 508 * @see #getTitle() 509 * 510 * @attr ref R.styleable#CollapsingToolbarLayout_title 511 */ 512 public void setTitle(@Nullable CharSequence title) { 513 mCollapsingTextHelper.setText(title); 514 } 515 516 /** 517 * Returns the title currently being displayed by this view. If the title is not enabled, then 518 * this will return {@code null}. 519 * 520 * @attr ref R.styleable#CollapsingToolbarLayout_title 521 */ 522 @Nullable 523 public CharSequence getTitle() { 524 return mCollapsingTitleEnabled ? mCollapsingTextHelper.getText() : null; 525 } 526 527 /** 528 * Sets whether this view should display its own title. 529 * 530 * <p>The title displayed by this view will shrink and grow based on the scroll offset.</p> 531 * 532 * @see #setTitle(CharSequence) 533 * @see #isTitleEnabled() 534 * 535 * @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled 536 */ 537 public void setTitleEnabled(boolean enabled) { 538 if (enabled != mCollapsingTitleEnabled) { 539 mCollapsingTitleEnabled = enabled; 540 updateDummyView(); 541 requestLayout(); 542 } 543 } 544 545 /** 546 * Returns whether this view is currently displaying its own title. 547 * 548 * @see #setTitleEnabled(boolean) 549 * 550 * @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled 551 */ 552 public boolean isTitleEnabled() { 553 return mCollapsingTitleEnabled; 554 } 555 556 /** 557 * Set whether the content scrim and/or status bar scrim should be shown or not. Any change 558 * in the vertical scroll may overwrite this value. Any visibility change will be animated if 559 * this view has already been laid out. 560 * 561 * @param shown whether the scrims should be shown 562 * 563 * @see #getStatusBarScrim() 564 * @see #getContentScrim() 565 */ 566 public void setScrimsShown(boolean shown) { 567 setScrimsShown(shown, ViewCompat.isLaidOut(this) && !isInEditMode()); 568 } 569 570 /** 571 * Set whether the content scrim and/or status bar scrim should be shown or not. Any change 572 * in the vertical scroll may overwrite this value. 573 * 574 * @param shown whether the scrims should be shown 575 * @param animate whether to animate the visibility change 576 * 577 * @see #getStatusBarScrim() 578 * @see #getContentScrim() 579 */ 580 public void setScrimsShown(boolean shown, boolean animate) { 581 if (mScrimsAreShown != shown) { 582 if (animate) { 583 animateScrim(shown ? 0xFF : 0x0); 584 } else { 585 setScrimAlpha(shown ? 0xFF : 0x0); 586 } 587 mScrimsAreShown = shown; 588 } 589 } 590 591 private void animateScrim(int targetAlpha) { 592 ensureToolbar(); 593 if (mScrimAnimator == null) { 594 mScrimAnimator = ViewUtils.createAnimator(); 595 mScrimAnimator.setDuration(mScrimAnimationDuration); 596 mScrimAnimator.setInterpolator( 597 targetAlpha > mScrimAlpha 598 ? AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR 599 : AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR); 600 mScrimAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { 601 @Override 602 public void onAnimationUpdate(ValueAnimatorCompat animator) { 603 setScrimAlpha(animator.getAnimatedIntValue()); 604 } 605 }); 606 } else if (mScrimAnimator.isRunning()) { 607 mScrimAnimator.cancel(); 608 } 609 610 mScrimAnimator.setIntValues(mScrimAlpha, targetAlpha); 611 mScrimAnimator.start(); 612 } 613 614 private void setScrimAlpha(int alpha) { 615 if (alpha != mScrimAlpha) { 616 final Drawable contentScrim = mContentScrim; 617 if (contentScrim != null && mToolbar != null) { 618 ViewCompat.postInvalidateOnAnimation(mToolbar); 619 } 620 mScrimAlpha = alpha; 621 ViewCompat.postInvalidateOnAnimation(CollapsingToolbarLayout.this); 622 } 623 } 624 625 /** 626 * Set the drawable to use for the content scrim from resources. Providing null will disable 627 * the scrim functionality. 628 * 629 * @param drawable the drawable to display 630 * 631 * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim 632 * @see #getContentScrim() 633 */ 634 public void setContentScrim(@Nullable Drawable drawable) { 635 if (mContentScrim != drawable) { 636 if (mContentScrim != null) { 637 mContentScrim.setCallback(null); 638 } 639 mContentScrim = drawable != null ? drawable.mutate() : null; 640 if (mContentScrim != null) { 641 mContentScrim.setBounds(0, 0, getWidth(), getHeight()); 642 mContentScrim.setCallback(this); 643 mContentScrim.setAlpha(mScrimAlpha); 644 } 645 ViewCompat.postInvalidateOnAnimation(this); 646 } 647 } 648 649 /** 650 * Set the color to use for the content scrim. 651 * 652 * @param color the color to display 653 * 654 * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim 655 * @see #getContentScrim() 656 */ 657 public void setContentScrimColor(@ColorInt int color) { 658 setContentScrim(new ColorDrawable(color)); 659 } 660 661 /** 662 * Set the drawable to use for the content scrim from resources. 663 * 664 * @param resId drawable resource id 665 * 666 * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim 667 * @see #getContentScrim() 668 */ 669 public void setContentScrimResource(@DrawableRes int resId) { 670 setContentScrim(ContextCompat.getDrawable(getContext(), resId)); 671 672 } 673 674 /** 675 * Returns the drawable which is used for the foreground scrim. 676 * 677 * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim 678 * @see #setContentScrim(Drawable) 679 */ 680 @Nullable 681 public Drawable getContentScrim() { 682 return mContentScrim; 683 } 684 685 /** 686 * Set the drawable to use for the status bar scrim from resources. 687 * Providing null will disable the scrim functionality. 688 * 689 * <p>This scrim is only shown when we have been given a top system inset.</p> 690 * 691 * @param drawable the drawable to display 692 * 693 * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim 694 * @see #getStatusBarScrim() 695 */ 696 public void setStatusBarScrim(@Nullable Drawable drawable) { 697 if (mStatusBarScrim != drawable) { 698 if (mStatusBarScrim != null) { 699 mStatusBarScrim.setCallback(null); 700 } 701 mStatusBarScrim = drawable != null ? drawable.mutate() : null; 702 if (mStatusBarScrim != null) { 703 if (mStatusBarScrim.isStateful()) { 704 mStatusBarScrim.setState(getDrawableState()); 705 } 706 DrawableCompat.setLayoutDirection(mStatusBarScrim, 707 ViewCompat.getLayoutDirection(this)); 708 mStatusBarScrim.setVisible(getVisibility() == VISIBLE, false); 709 mStatusBarScrim.setCallback(this); 710 mStatusBarScrim.setAlpha(mScrimAlpha); 711 } 712 ViewCompat.postInvalidateOnAnimation(this); 713 } 714 } 715 716 @Override 717 protected void drawableStateChanged() { 718 super.drawableStateChanged(); 719 720 final int[] state = getDrawableState(); 721 boolean changed = false; 722 723 Drawable d = mStatusBarScrim; 724 if (d != null && d.isStateful()) { 725 changed |= d.setState(state); 726 } 727 d = mContentScrim; 728 if (d != null && d.isStateful()) { 729 changed |= d.setState(state); 730 } 731 if (mCollapsingTextHelper != null) { 732 changed |= mCollapsingTextHelper.setState(state); 733 } 734 735 if (changed) { 736 invalidate(); 737 } 738 } 739 740 @Override 741 protected boolean verifyDrawable(Drawable who) { 742 return super.verifyDrawable(who) || who == mContentScrim || who == mStatusBarScrim; 743 } 744 745 @Override 746 public void setVisibility(int visibility) { 747 super.setVisibility(visibility); 748 749 final boolean visible = visibility == VISIBLE; 750 if (mStatusBarScrim != null && mStatusBarScrim.isVisible() != visible) { 751 mStatusBarScrim.setVisible(visible, false); 752 } 753 if (mContentScrim != null && mContentScrim.isVisible() != visible) { 754 mContentScrim.setVisible(visible, false); 755 } 756 } 757 758 /** 759 * Set the color to use for the status bar scrim. 760 * 761 * <p>This scrim is only shown when we have been given a top system inset.</p> 762 * 763 * @param color the color to display 764 * 765 * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim 766 * @see #getStatusBarScrim() 767 */ 768 public void setStatusBarScrimColor(@ColorInt int color) { 769 setStatusBarScrim(new ColorDrawable(color)); 770 } 771 772 /** 773 * Set the drawable to use for the content scrim from resources. 774 * 775 * @param resId drawable resource id 776 * 777 * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim 778 * @see #getStatusBarScrim() 779 */ 780 public void setStatusBarScrimResource(@DrawableRes int resId) { 781 setStatusBarScrim(ContextCompat.getDrawable(getContext(), resId)); 782 } 783 784 /** 785 * Returns the drawable which is used for the status bar scrim. 786 * 787 * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim 788 * @see #setStatusBarScrim(Drawable) 789 */ 790 @Nullable 791 public Drawable getStatusBarScrim() { 792 return mStatusBarScrim; 793 } 794 795 /** 796 * Sets the text color and size for the collapsed title from the specified 797 * TextAppearance resource. 798 * 799 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance 800 */ 801 public void setCollapsedTitleTextAppearance(@StyleRes int resId) { 802 mCollapsingTextHelper.setCollapsedTextAppearance(resId); 803 } 804 805 /** 806 * Sets the text color of the collapsed title. 807 * 808 * @param color The new text color in ARGB format 809 */ 810 public void setCollapsedTitleTextColor(@ColorInt int color) { 811 setCollapsedTitleTextColor(ColorStateList.valueOf(color)); 812 } 813 814 /** 815 * Sets the text colors of the collapsed title. 816 * 817 * @param colors ColorStateList containing the new text colors 818 */ 819 public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) { 820 mCollapsingTextHelper.setCollapsedTextColor(colors); 821 } 822 823 /** 824 * Sets the horizontal alignment of the collapsed title and the vertical gravity that will 825 * be used when there is extra space in the collapsed bounds beyond what is required for 826 * the title itself. 827 * 828 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity 829 */ 830 public void setCollapsedTitleGravity(int gravity) { 831 mCollapsingTextHelper.setCollapsedTextGravity(gravity); 832 } 833 834 /** 835 * Returns the horizontal and vertical alignment for title when collapsed. 836 * 837 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity 838 */ 839 public int getCollapsedTitleGravity() { 840 return mCollapsingTextHelper.getCollapsedTextGravity(); 841 } 842 843 /** 844 * Sets the text color and size for the expanded title from the specified 845 * TextAppearance resource. 846 * 847 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance 848 */ 849 public void setExpandedTitleTextAppearance(@StyleRes int resId) { 850 mCollapsingTextHelper.setExpandedTextAppearance(resId); 851 } 852 853 /** 854 * Sets the text color of the expanded title. 855 * 856 * @param color The new text color in ARGB format 857 */ 858 public void setExpandedTitleColor(@ColorInt int color) { 859 setExpandedTitleTextColor(ColorStateList.valueOf(color)); 860 } 861 862 /** 863 * Sets the text colors of the expanded title. 864 * 865 * @param colors ColorStateList containing the new text colors 866 */ 867 public void setExpandedTitleTextColor(@NonNull ColorStateList colors) { 868 mCollapsingTextHelper.setExpandedTextColor(colors); 869 } 870 871 /** 872 * Sets the horizontal alignment of the expanded title and the vertical gravity that will 873 * be used when there is extra space in the expanded bounds beyond what is required for 874 * the title itself. 875 * 876 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity 877 */ 878 public void setExpandedTitleGravity(int gravity) { 879 mCollapsingTextHelper.setExpandedTextGravity(gravity); 880 } 881 882 /** 883 * Returns the horizontal and vertical alignment for title when expanded. 884 * 885 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity 886 */ 887 public int getExpandedTitleGravity() { 888 return mCollapsingTextHelper.getExpandedTextGravity(); 889 } 890 891 /** 892 * Set the typeface to use for the collapsed title. 893 * 894 * @param typeface typeface to use, or {@code null} to use the default. 895 */ 896 public void setCollapsedTitleTypeface(@Nullable Typeface typeface) { 897 mCollapsingTextHelper.setCollapsedTypeface(typeface); 898 } 899 900 /** 901 * Returns the typeface used for the collapsed title. 902 */ 903 @NonNull 904 public Typeface getCollapsedTitleTypeface() { 905 return mCollapsingTextHelper.getCollapsedTypeface(); 906 } 907 908 /** 909 * Set the typeface to use for the expanded title. 910 * 911 * @param typeface typeface to use, or {@code null} to use the default. 912 */ 913 public void setExpandedTitleTypeface(@Nullable Typeface typeface) { 914 mCollapsingTextHelper.setExpandedTypeface(typeface); 915 } 916 917 /** 918 * Returns the typeface used for the expanded title. 919 */ 920 @NonNull 921 public Typeface getExpandedTitleTypeface() { 922 return mCollapsingTextHelper.getExpandedTypeface(); 923 } 924 925 /** 926 * Sets the expanded title margins. 927 * 928 * @param start the starting title margin in pixels 929 * @param top the top title margin in pixels 930 * @param end the ending title margin in pixels 931 * @param bottom the bottom title margin in pixels 932 * 933 * @see #getExpandedTitleMarginStart() 934 * @see #getExpandedTitleMarginTop() 935 * @see #getExpandedTitleMarginEnd() 936 * @see #getExpandedTitleMarginBottom() 937 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMargin 938 */ 939 public void setExpandedTitleMargin(int start, int top, int end, int bottom) { 940 mExpandedMarginStart = start; 941 mExpandedMarginTop = top; 942 mExpandedMarginEnd = end; 943 mExpandedMarginBottom = bottom; 944 requestLayout(); 945 } 946 947 /** 948 * @return the starting expanded title margin in pixels 949 * 950 * @see #setExpandedTitleMarginStart(int) 951 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart 952 */ 953 public int getExpandedTitleMarginStart() { 954 return mExpandedMarginStart; 955 } 956 957 /** 958 * Sets the starting expanded title margin in pixels. 959 * 960 * @param margin the starting title margin in pixels 961 * @see #getExpandedTitleMarginStart() 962 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart 963 */ 964 public void setExpandedTitleMarginStart(int margin) { 965 mExpandedMarginStart = margin; 966 requestLayout(); 967 } 968 969 /** 970 * @return the top expanded title margin in pixels 971 * @see #setExpandedTitleMarginTop(int) 972 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop 973 */ 974 public int getExpandedTitleMarginTop() { 975 return mExpandedMarginTop; 976 } 977 978 /** 979 * Sets the top expanded title margin in pixels. 980 * 981 * @param margin the top title margin in pixels 982 * @see #getExpandedTitleMarginTop() 983 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop 984 */ 985 public void setExpandedTitleMarginTop(int margin) { 986 mExpandedMarginTop = margin; 987 requestLayout(); 988 } 989 990 /** 991 * @return the ending expanded title margin in pixels 992 * @see #setExpandedTitleMarginEnd(int) 993 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd 994 */ 995 public int getExpandedTitleMarginEnd() { 996 return mExpandedMarginEnd; 997 } 998 999 /** 1000 * Sets the ending expanded title margin in pixels. 1001 * 1002 * @param margin the ending title margin in pixels 1003 * @see #getExpandedTitleMarginEnd() 1004 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd 1005 */ 1006 public void setExpandedTitleMarginEnd(int margin) { 1007 mExpandedMarginEnd = margin; 1008 requestLayout(); 1009 } 1010 1011 /** 1012 * @return the bottom expanded title margin in pixels 1013 * @see #setExpandedTitleMarginBottom(int) 1014 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom 1015 */ 1016 public int getExpandedTitleMarginBottom() { 1017 return mExpandedMarginBottom; 1018 } 1019 1020 /** 1021 * Sets the bottom expanded title margin in pixels. 1022 * 1023 * @param margin the bottom title margin in pixels 1024 * @see #getExpandedTitleMarginBottom() 1025 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom 1026 */ 1027 public void setExpandedTitleMarginBottom(int margin) { 1028 mExpandedMarginBottom = margin; 1029 requestLayout(); 1030 } 1031 1032 /** 1033 * Set the amount of visible height in pixels used to define when to trigger a scrim 1034 * visibility change. 1035 * 1036 * <p>If the visible height of this view is less than the given value, the scrims will be 1037 * made visible, otherwise they are hidden.</p> 1038 * 1039 * @param height value in pixels used to define when to trigger a scrim visibility change 1040 * 1041 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_scrimVisibleHeightTrigger 1042 */ 1043 public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) { 1044 if (mScrimVisibleHeightTrigger != height) { 1045 mScrimVisibleHeightTrigger = height; 1046 // Update the scrim visibility 1047 updateScrimVisibility(); 1048 } 1049 } 1050 1051 /** 1052 * Returns the amount of visible height in pixels used to define when to trigger a scrim 1053 * visibility change. 1054 * 1055 * @see #setScrimVisibleHeightTrigger(int) 1056 */ 1057 public int getScrimVisibleHeightTrigger() { 1058 if (mScrimVisibleHeightTrigger >= 0) { 1059 // If we have one explicitly set, return it 1060 return mScrimVisibleHeightTrigger; 1061 } 1062 1063 // Otherwise we'll use the default computed value 1064 final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 1065 1066 final int minHeight = ViewCompat.getMinimumHeight(this); 1067 if (minHeight > 0) { 1068 // If we have a minHeight set, lets use 2 * minHeight (capped at our height) 1069 return Math.min((minHeight * 2) + insetTop, getHeight()); 1070 } 1071 1072 // If we reach here then we don't have a min height set. Instead we'll take a 1073 // guess at 1/3 of our height being visible 1074 return getHeight() / 3; 1075 } 1076 1077 /** 1078 * Set the duration used for scrim visibility animations. 1079 * 1080 * @param duration the duration to use in milliseconds 1081 * 1082 * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration 1083 */ 1084 public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) { 1085 mScrimAnimationDuration = duration; 1086 } 1087 1088 /** 1089 * Returns the duration in milliseconds used for scrim visibility animations. 1090 */ 1091 public long getScrimAnimationDuration() { 1092 return mScrimAnimationDuration; 1093 } 1094 1095 @Override 1096 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1097 return p instanceof LayoutParams; 1098 } 1099 1100 @Override 1101 protected LayoutParams generateDefaultLayoutParams() { 1102 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 1103 } 1104 1105 @Override 1106 public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { 1107 return new LayoutParams(getContext(), attrs); 1108 } 1109 1110 @Override 1111 protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1112 return new LayoutParams(p); 1113 } 1114 1115 public static class LayoutParams extends FrameLayout.LayoutParams { 1116 1117 private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f; 1118 1119 /** @hide */ 1120 @IntDef({ 1121 COLLAPSE_MODE_OFF, 1122 COLLAPSE_MODE_PIN, 1123 COLLAPSE_MODE_PARALLAX 1124 }) 1125 @Retention(RetentionPolicy.SOURCE) 1126 @interface CollapseMode {} 1127 1128 /** 1129 * The view will act as normal with no collapsing behavior. 1130 */ 1131 public static final int COLLAPSE_MODE_OFF = 0; 1132 1133 /** 1134 * The view will pin in place until it reaches the bottom of the 1135 * {@link CollapsingToolbarLayout}. 1136 */ 1137 public static final int COLLAPSE_MODE_PIN = 1; 1138 1139 /** 1140 * The view will scroll in a parallax fashion. See {@link #setParallaxMultiplier(float)} 1141 * to change the multiplier used. 1142 */ 1143 public static final int COLLAPSE_MODE_PARALLAX = 2; 1144 1145 int mCollapseMode = COLLAPSE_MODE_OFF; 1146 float mParallaxMult = DEFAULT_PARALLAX_MULTIPLIER; 1147 1148 public LayoutParams(Context c, AttributeSet attrs) { 1149 super(c, attrs); 1150 1151 TypedArray a = c.obtainStyledAttributes(attrs, 1152 R.styleable.CollapsingToolbarLayout_Layout); 1153 mCollapseMode = a.getInt( 1154 R.styleable.CollapsingToolbarLayout_Layout_layout_collapseMode, 1155 COLLAPSE_MODE_OFF); 1156 setParallaxMultiplier(a.getFloat( 1157 R.styleable.CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier, 1158 DEFAULT_PARALLAX_MULTIPLIER)); 1159 a.recycle(); 1160 } 1161 1162 public LayoutParams(int width, int height) { 1163 super(width, height); 1164 } 1165 1166 public LayoutParams(int width, int height, int gravity) { 1167 super(width, height, gravity); 1168 } 1169 1170 public LayoutParams(ViewGroup.LayoutParams p) { 1171 super(p); 1172 } 1173 1174 public LayoutParams(MarginLayoutParams source) { 1175 super(source); 1176 } 1177 1178 public LayoutParams(FrameLayout.LayoutParams source) { 1179 super(source); 1180 } 1181 1182 /** 1183 * Set the collapse mode. 1184 * 1185 * @param collapseMode one of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN} 1186 * or {@link #COLLAPSE_MODE_PARALLAX}. 1187 */ 1188 public void setCollapseMode(@CollapseMode int collapseMode) { 1189 mCollapseMode = collapseMode; 1190 } 1191 1192 /** 1193 * Returns the requested collapse mode. 1194 * 1195 * @return the current mode. One of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN} 1196 * or {@link #COLLAPSE_MODE_PARALLAX}. 1197 */ 1198 @CollapseMode 1199 public int getCollapseMode() { 1200 return mCollapseMode; 1201 } 1202 1203 /** 1204 * Set the parallax scroll multiplier used in conjunction with 1205 * {@link #COLLAPSE_MODE_PARALLAX}. A value of {@code 0.0} indicates no movement at all, 1206 * {@code 1.0f} indicates normal scroll movement. 1207 * 1208 * @param multiplier the multiplier. 1209 * 1210 * @see #getParallaxMultiplier() 1211 */ 1212 public void setParallaxMultiplier(float multiplier) { 1213 mParallaxMult = multiplier; 1214 } 1215 1216 /** 1217 * Returns the parallax scroll multiplier used in conjunction with 1218 * {@link #COLLAPSE_MODE_PARALLAX}. 1219 * 1220 * @see #setParallaxMultiplier(float) 1221 */ 1222 public float getParallaxMultiplier() { 1223 return mParallaxMult; 1224 } 1225 } 1226 1227 /** 1228 * Show or hide the scrims if needed 1229 */ 1230 final void updateScrimVisibility() { 1231 if (mContentScrim != null || mStatusBarScrim != null) { 1232 setScrimsShown(getHeight() + mCurrentOffset < getScrimVisibleHeightTrigger()); 1233 } 1234 } 1235 1236 final int getMaxOffsetForPinChild(View child) { 1237 final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child); 1238 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1239 return getHeight() 1240 - offsetHelper.getLayoutTop() 1241 - child.getHeight() 1242 - lp.bottomMargin; 1243 } 1244 1245 private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener { 1246 @Override 1247 public void onOffsetChanged(AppBarLayout layout, int verticalOffset) { 1248 mCurrentOffset = verticalOffset; 1249 1250 final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0; 1251 1252 for (int i = 0, z = getChildCount(); i < z; i++) { 1253 final View child = getChildAt(i); 1254 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1255 final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child); 1256 1257 switch (lp.mCollapseMode) { 1258 case LayoutParams.COLLAPSE_MODE_PIN: 1259 offsetHelper.setTopAndBottomOffset( 1260 constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child))); 1261 break; 1262 case LayoutParams.COLLAPSE_MODE_PARALLAX: 1263 offsetHelper.setTopAndBottomOffset( 1264 Math.round(-verticalOffset * lp.mParallaxMult)); 1265 break; 1266 } 1267 } 1268 1269 // Show or hide the scrims if needed 1270 updateScrimVisibility(); 1271 1272 if (mStatusBarScrim != null && insetTop > 0) { 1273 ViewCompat.postInvalidateOnAnimation(CollapsingToolbarLayout.this); 1274 } 1275 1276 // Update the collapsing text's fraction 1277 final int expandRange = getHeight() - ViewCompat.getMinimumHeight( 1278 CollapsingToolbarLayout.this) - insetTop; 1279 mCollapsingTextHelper.setExpansionFraction( 1280 Math.abs(verticalOffset) / (float) expandRange); 1281 } 1282 } 1283} 1284