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