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