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