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