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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20import static android.support.v4.utils.ObjectUtils.objectEquals;
21
22import android.content.Context;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.Canvas;
26import android.graphics.Color;
27import android.graphics.Paint;
28import android.graphics.Rect;
29import android.graphics.Region;
30import android.graphics.drawable.ColorDrawable;
31import android.graphics.drawable.Drawable;
32import android.os.Build;
33import android.os.Parcel;
34import android.os.Parcelable;
35import android.os.SystemClock;
36import android.support.annotation.ColorInt;
37import android.support.annotation.DrawableRes;
38import android.support.annotation.FloatRange;
39import android.support.annotation.IdRes;
40import android.support.annotation.IntDef;
41import android.support.annotation.NonNull;
42import android.support.annotation.Nullable;
43import android.support.annotation.RestrictTo;
44import android.support.annotation.VisibleForTesting;
45import android.support.design.R;
46import android.support.v4.content.ContextCompat;
47import android.support.v4.graphics.drawable.DrawableCompat;
48import android.support.v4.math.MathUtils;
49import android.support.v4.util.Pools;
50import android.support.v4.view.AbsSavedState;
51import android.support.v4.view.GravityCompat;
52import android.support.v4.view.NestedScrollingParent;
53import android.support.v4.view.NestedScrollingParent2;
54import android.support.v4.view.NestedScrollingParentHelper;
55import android.support.v4.view.ViewCompat;
56import android.support.v4.view.ViewCompat.NestedScrollType;
57import android.support.v4.view.ViewCompat.ScrollAxis;
58import android.support.v4.view.WindowInsetsCompat;
59import android.text.TextUtils;
60import android.util.AttributeSet;
61import android.util.Log;
62import android.util.SparseArray;
63import android.view.Gravity;
64import android.view.MotionEvent;
65import android.view.View;
66import android.view.ViewGroup;
67import android.view.ViewParent;
68import android.view.ViewTreeObserver;
69
70import java.lang.annotation.Retention;
71import java.lang.annotation.RetentionPolicy;
72import java.lang.reflect.Constructor;
73import java.util.ArrayList;
74import java.util.Collections;
75import java.util.Comparator;
76import java.util.HashMap;
77import java.util.List;
78import java.util.Map;
79
80/**
81 * CoordinatorLayout is a super-powered {@link android.widget.FrameLayout FrameLayout}.
82 *
83 * <p>CoordinatorLayout is intended for two primary use cases:</p>
84 * <ol>
85 *     <li>As a top-level application decor or chrome layout</li>
86 *     <li>As a container for a specific interaction with one or more child views</li>
87 * </ol>
88 *
89 * <p>By specifying {@link CoordinatorLayout.Behavior Behaviors} for child views of a
90 * CoordinatorLayout you can provide many different interactions within a single parent and those
91 * views can also interact with one another. View classes can specify a default behavior when
92 * used as a child of a CoordinatorLayout using the
93 * {@link CoordinatorLayout.DefaultBehavior DefaultBehavior} annotation.</p>
94 *
95 * <p>Behaviors may be used to implement a variety of interactions and additional layout
96 * modifications ranging from sliding drawers and panels to swipe-dismissable elements and buttons
97 * that stick to other elements as they move and animate.</p>
98 *
99 * <p>Children of a CoordinatorLayout may have an
100 * {@link CoordinatorLayout.LayoutParams#setAnchorId(int) anchor}. This view id must correspond
101 * to an arbitrary descendant of the CoordinatorLayout, but it may not be the anchored child itself
102 * or a descendant of the anchored child. This can be used to place floating views relative to
103 * other arbitrary content panes.</p>
104 *
105 * <p>Children can specify {@link CoordinatorLayout.LayoutParams#insetEdge} to describe how the
106 * view insets the CoordinatorLayout. Any child views which are set to dodge the same inset edges by
107 * {@link CoordinatorLayout.LayoutParams#dodgeInsetEdges} will be moved appropriately so that the
108 * views do not overlap.</p>
109 */
110public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2 {
111    static final String TAG = "CoordinatorLayout";
112    static final String WIDGET_PACKAGE_NAME;
113
114    static {
115        final Package pkg = CoordinatorLayout.class.getPackage();
116        WIDGET_PACKAGE_NAME = pkg != null ? pkg.getName() : null;
117    }
118
119    private static final int TYPE_ON_INTERCEPT = 0;
120    private static final int TYPE_ON_TOUCH = 1;
121
122    static {
123        if (Build.VERSION.SDK_INT >= 21) {
124            TOP_SORTED_CHILDREN_COMPARATOR = new ViewElevationComparator();
125        } else {
126            TOP_SORTED_CHILDREN_COMPARATOR = null;
127        }
128    }
129
130    static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
131            Context.class,
132            AttributeSet.class
133    };
134
135    static final ThreadLocal<Map<String, Constructor<Behavior>>> sConstructors =
136            new ThreadLocal<>();
137
138    static final int EVENT_PRE_DRAW = 0;
139    static final int EVENT_NESTED_SCROLL = 1;
140    static final int EVENT_VIEW_REMOVED = 2;
141
142    /** @hide */
143    @RestrictTo(LIBRARY_GROUP)
144    @Retention(RetentionPolicy.SOURCE)
145    @IntDef({EVENT_PRE_DRAW, EVENT_NESTED_SCROLL, EVENT_VIEW_REMOVED})
146    public @interface DispatchChangeEvent {}
147
148    static final Comparator<View> TOP_SORTED_CHILDREN_COMPARATOR;
149    private static final Pools.Pool<Rect> sRectPool = new Pools.SynchronizedPool<>(12);
150
151    @NonNull
152    private static Rect acquireTempRect() {
153        Rect rect = sRectPool.acquire();
154        if (rect == null) {
155            rect = new Rect();
156        }
157        return rect;
158    }
159
160    private static void releaseTempRect(@NonNull Rect rect) {
161        rect.setEmpty();
162        sRectPool.release(rect);
163    }
164
165    private final List<View> mDependencySortedChildren = new ArrayList<>();
166    private final DirectedAcyclicGraph<View> mChildDag = new DirectedAcyclicGraph<>();
167
168    private final List<View> mTempList1 = new ArrayList<>();
169    private final List<View> mTempDependenciesList = new ArrayList<>();
170    private final int[] mTempIntPair = new int[2];
171    private Paint mScrimPaint;
172
173    private boolean mDisallowInterceptReset;
174
175    private boolean mIsAttachedToWindow;
176
177    private int[] mKeylines;
178
179    private View mBehaviorTouchView;
180    private View mNestedScrollingTarget;
181
182    private OnPreDrawListener mOnPreDrawListener;
183    private boolean mNeedsPreDrawListener;
184
185    private WindowInsetsCompat mLastInsets;
186    private boolean mDrawStatusBarBackground;
187    private Drawable mStatusBarBackground;
188
189    OnHierarchyChangeListener mOnHierarchyChangeListener;
190    private android.support.v4.view.OnApplyWindowInsetsListener mApplyWindowInsetsListener;
191
192    private final NestedScrollingParentHelper mNestedScrollingParentHelper =
193            new NestedScrollingParentHelper(this);
194
195    public CoordinatorLayout(Context context) {
196        this(context, null);
197    }
198
199    public CoordinatorLayout(Context context, AttributeSet attrs) {
200        this(context, attrs, 0);
201    }
202
203    public CoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
204        super(context, attrs, defStyleAttr);
205
206        ThemeUtils.checkAppCompatTheme(context);
207
208        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout,
209                defStyleAttr, R.style.Widget_Design_CoordinatorLayout);
210        final int keylineArrayRes = a.getResourceId(R.styleable.CoordinatorLayout_keylines, 0);
211        if (keylineArrayRes != 0) {
212            final Resources res = context.getResources();
213            mKeylines = res.getIntArray(keylineArrayRes);
214            final float density = res.getDisplayMetrics().density;
215            final int count = mKeylines.length;
216            for (int i = 0; i < count; i++) {
217                mKeylines[i] = (int) (mKeylines[i] * density);
218            }
219        }
220        mStatusBarBackground = a.getDrawable(R.styleable.CoordinatorLayout_statusBarBackground);
221        a.recycle();
222
223        setupForInsets();
224        super.setOnHierarchyChangeListener(new HierarchyChangeListener());
225    }
226
227    @Override
228    public void setOnHierarchyChangeListener(OnHierarchyChangeListener onHierarchyChangeListener) {
229        mOnHierarchyChangeListener = onHierarchyChangeListener;
230    }
231
232    @Override
233    public void onAttachedToWindow() {
234        super.onAttachedToWindow();
235        resetTouchBehaviors();
236        if (mNeedsPreDrawListener) {
237            if (mOnPreDrawListener == null) {
238                mOnPreDrawListener = new OnPreDrawListener();
239            }
240            final ViewTreeObserver vto = getViewTreeObserver();
241            vto.addOnPreDrawListener(mOnPreDrawListener);
242        }
243        if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) {
244            // We're set to fitSystemWindows but we haven't had any insets yet...
245            // We should request a new dispatch of window insets
246            ViewCompat.requestApplyInsets(this);
247        }
248        mIsAttachedToWindow = true;
249    }
250
251    @Override
252    public void onDetachedFromWindow() {
253        super.onDetachedFromWindow();
254        resetTouchBehaviors();
255        if (mNeedsPreDrawListener && mOnPreDrawListener != null) {
256            final ViewTreeObserver vto = getViewTreeObserver();
257            vto.removeOnPreDrawListener(mOnPreDrawListener);
258        }
259        if (mNestedScrollingTarget != null) {
260            onStopNestedScroll(mNestedScrollingTarget);
261        }
262        mIsAttachedToWindow = false;
263    }
264
265    /**
266     * Set a drawable to draw in the insets area for the status bar.
267     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
268     *
269     * @param bg Background drawable to draw behind the status bar
270     */
271    public void setStatusBarBackground(@Nullable final Drawable bg) {
272        if (mStatusBarBackground != bg) {
273            if (mStatusBarBackground != null) {
274                mStatusBarBackground.setCallback(null);
275            }
276            mStatusBarBackground = bg != null ? bg.mutate() : null;
277            if (mStatusBarBackground != null) {
278                if (mStatusBarBackground.isStateful()) {
279                    mStatusBarBackground.setState(getDrawableState());
280                }
281                DrawableCompat.setLayoutDirection(mStatusBarBackground,
282                        ViewCompat.getLayoutDirection(this));
283                mStatusBarBackground.setVisible(getVisibility() == VISIBLE, false);
284                mStatusBarBackground.setCallback(this);
285            }
286            ViewCompat.postInvalidateOnAnimation(this);
287        }
288    }
289
290    /**
291     * Gets the drawable used to draw in the insets area for the status bar.
292     *
293     * @return The status bar background drawable, or null if none set
294     */
295    @Nullable
296    public Drawable getStatusBarBackground() {
297        return mStatusBarBackground;
298    }
299
300    @Override
301    protected void drawableStateChanged() {
302        super.drawableStateChanged();
303
304        final int[] state = getDrawableState();
305        boolean changed = false;
306
307        Drawable d = mStatusBarBackground;
308        if (d != null && d.isStateful()) {
309            changed |= d.setState(state);
310        }
311
312        if (changed) {
313            invalidate();
314        }
315    }
316
317    @Override
318    protected boolean verifyDrawable(Drawable who) {
319        return super.verifyDrawable(who) || who == mStatusBarBackground;
320    }
321
322    @Override
323    public void setVisibility(int visibility) {
324        super.setVisibility(visibility);
325
326        final boolean visible = visibility == VISIBLE;
327        if (mStatusBarBackground != null && mStatusBarBackground.isVisible() != visible) {
328            mStatusBarBackground.setVisible(visible, false);
329        }
330    }
331
332    /**
333     * Set a drawable to draw in the insets area for the status bar.
334     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
335     *
336     * @param resId Resource id of a background drawable to draw behind the status bar
337     */
338    public void setStatusBarBackgroundResource(@DrawableRes int resId) {
339        setStatusBarBackground(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null);
340    }
341
342    /**
343     * Set a drawable to draw in the insets area for the status bar.
344     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
345     *
346     * @param color Color to use as a background drawable to draw behind the status bar
347     *              in 0xAARRGGBB format.
348     */
349    public void setStatusBarBackgroundColor(@ColorInt int color) {
350        setStatusBarBackground(new ColorDrawable(color));
351    }
352
353    final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
354        if (!objectEquals(mLastInsets, insets)) {
355            mLastInsets = insets;
356            mDrawStatusBarBackground = insets != null && insets.getSystemWindowInsetTop() > 0;
357            setWillNotDraw(!mDrawStatusBarBackground && getBackground() == null);
358
359            // Now dispatch to the Behaviors
360            insets = dispatchApplyWindowInsetsToBehaviors(insets);
361            requestLayout();
362        }
363        return insets;
364    }
365
366    final WindowInsetsCompat getLastWindowInsets() {
367        return mLastInsets;
368    }
369
370    /**
371     * Reset all Behavior-related tracking records either to clean up or in preparation
372     * for a new event stream. This should be called when attached or detached from a window,
373     * in response to an UP or CANCEL event, when intercept is request-disallowed
374     * and similar cases where an event stream in progress will be aborted.
375     */
376    private void resetTouchBehaviors() {
377        if (mBehaviorTouchView != null) {
378            final Behavior b = ((LayoutParams) mBehaviorTouchView.getLayoutParams()).getBehavior();
379            if (b != null) {
380                final long now = SystemClock.uptimeMillis();
381                final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
382                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
383                b.onTouchEvent(this, mBehaviorTouchView, cancelEvent);
384                cancelEvent.recycle();
385            }
386            mBehaviorTouchView = null;
387        }
388
389        final int childCount = getChildCount();
390        for (int i = 0; i < childCount; i++) {
391            final View child = getChildAt(i);
392            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
393            lp.resetTouchBehaviorTracking();
394        }
395        mDisallowInterceptReset = false;
396    }
397
398    /**
399     * Populate a list with the current child views, sorted such that the topmost views
400     * in z-order are at the front of the list. Useful for hit testing and event dispatch.
401     */
402    private void getTopSortedChildren(List<View> out) {
403        out.clear();
404
405        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
406        final int childCount = getChildCount();
407        for (int i = childCount - 1; i >= 0; i--) {
408            final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i;
409            final View child = getChildAt(childIndex);
410            out.add(child);
411        }
412
413        if (TOP_SORTED_CHILDREN_COMPARATOR != null) {
414            Collections.sort(out, TOP_SORTED_CHILDREN_COMPARATOR);
415        }
416    }
417
418    private boolean performIntercept(MotionEvent ev, final int type) {
419        boolean intercepted = false;
420        boolean newBlock = false;
421
422        MotionEvent cancelEvent = null;
423
424        final int action = ev.getActionMasked();
425
426        final List<View> topmostChildList = mTempList1;
427        getTopSortedChildren(topmostChildList);
428
429        // Let topmost child views inspect first
430        final int childCount = topmostChildList.size();
431        for (int i = 0; i < childCount; i++) {
432            final View child = topmostChildList.get(i);
433            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
434            final Behavior b = lp.getBehavior();
435
436            if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {
437                // Cancel all behaviors beneath the one that intercepted.
438                // If the event is "down" then we don't have anything to cancel yet.
439                if (b != null) {
440                    if (cancelEvent == null) {
441                        final long now = SystemClock.uptimeMillis();
442                        cancelEvent = MotionEvent.obtain(now, now,
443                                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
444                    }
445                    switch (type) {
446                        case TYPE_ON_INTERCEPT:
447                            b.onInterceptTouchEvent(this, child, cancelEvent);
448                            break;
449                        case TYPE_ON_TOUCH:
450                            b.onTouchEvent(this, child, cancelEvent);
451                            break;
452                    }
453                }
454                continue;
455            }
456
457            if (!intercepted && b != null) {
458                switch (type) {
459                    case TYPE_ON_INTERCEPT:
460                        intercepted = b.onInterceptTouchEvent(this, child, ev);
461                        break;
462                    case TYPE_ON_TOUCH:
463                        intercepted = b.onTouchEvent(this, child, ev);
464                        break;
465                }
466                if (intercepted) {
467                    mBehaviorTouchView = child;
468                }
469            }
470
471            // Don't keep going if we're not allowing interaction below this.
472            // Setting newBlock will make sure we cancel the rest of the behaviors.
473            final boolean wasBlocking = lp.didBlockInteraction();
474            final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
475            newBlock = isBlocking && !wasBlocking;
476            if (isBlocking && !newBlock) {
477                // Stop here since we don't have anything more to cancel - we already did
478                // when the behavior first started blocking things below this point.
479                break;
480            }
481        }
482
483        topmostChildList.clear();
484
485        return intercepted;
486    }
487
488    @Override
489    public boolean onInterceptTouchEvent(MotionEvent ev) {
490        MotionEvent cancelEvent = null;
491
492        final int action = ev.getActionMasked();
493
494        // Make sure we reset in case we had missed a previous important event.
495        if (action == MotionEvent.ACTION_DOWN) {
496            resetTouchBehaviors();
497        }
498
499        final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);
500
501        if (cancelEvent != null) {
502            cancelEvent.recycle();
503        }
504
505        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
506            resetTouchBehaviors();
507        }
508
509        return intercepted;
510    }
511
512    @Override
513    public boolean onTouchEvent(MotionEvent ev) {
514        boolean handled = false;
515        boolean cancelSuper = false;
516        MotionEvent cancelEvent = null;
517
518        final int action = ev.getActionMasked();
519
520        if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
521            // Safe since performIntercept guarantees that
522            // mBehaviorTouchView != null if it returns true
523            final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
524            final Behavior b = lp.getBehavior();
525            if (b != null) {
526                handled = b.onTouchEvent(this, mBehaviorTouchView, ev);
527            }
528        }
529
530        // Keep the super implementation correct
531        if (mBehaviorTouchView == null) {
532            handled |= super.onTouchEvent(ev);
533        } else if (cancelSuper) {
534            if (cancelEvent == null) {
535                final long now = SystemClock.uptimeMillis();
536                cancelEvent = MotionEvent.obtain(now, now,
537                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
538            }
539            super.onTouchEvent(cancelEvent);
540        }
541
542        if (!handled && action == MotionEvent.ACTION_DOWN) {
543
544        }
545
546        if (cancelEvent != null) {
547            cancelEvent.recycle();
548        }
549
550        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
551            resetTouchBehaviors();
552        }
553
554        return handled;
555    }
556
557    @Override
558    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
559        super.requestDisallowInterceptTouchEvent(disallowIntercept);
560        if (disallowIntercept && !mDisallowInterceptReset) {
561            resetTouchBehaviors();
562            mDisallowInterceptReset = true;
563        }
564    }
565
566    private int getKeyline(int index) {
567        if (mKeylines == null) {
568            Log.e(TAG, "No keylines defined for " + this + " - attempted index lookup " + index);
569            return 0;
570        }
571
572        if (index < 0 || index >= mKeylines.length) {
573            Log.e(TAG, "Keyline index " + index + " out of range for " + this);
574            return 0;
575        }
576
577        return mKeylines[index];
578    }
579
580    static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
581        if (TextUtils.isEmpty(name)) {
582            return null;
583        }
584
585        final String fullName;
586        if (name.startsWith(".")) {
587            // Relative to the app package. Prepend the app package name.
588            fullName = context.getPackageName() + name;
589        } else if (name.indexOf('.') >= 0) {
590            // Fully qualified package name.
591            fullName = name;
592        } else {
593            // Assume stock behavior in this package (if we have one)
594            fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
595                    ? (WIDGET_PACKAGE_NAME + '.' + name)
596                    : name;
597        }
598
599        try {
600            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
601            if (constructors == null) {
602                constructors = new HashMap<>();
603                sConstructors.set(constructors);
604            }
605            Constructor<Behavior> c = constructors.get(fullName);
606            if (c == null) {
607                final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
608                        context.getClassLoader());
609                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
610                c.setAccessible(true);
611                constructors.put(fullName, c);
612            }
613            return c.newInstance(context, attrs);
614        } catch (Exception e) {
615            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
616        }
617    }
618
619    LayoutParams getResolvedLayoutParams(View child) {
620        final LayoutParams result = (LayoutParams) child.getLayoutParams();
621        if (!result.mBehaviorResolved) {
622            Class<?> childClass = child.getClass();
623            DefaultBehavior defaultBehavior = null;
624            while (childClass != null &&
625                    (defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
626                childClass = childClass.getSuperclass();
627            }
628            if (defaultBehavior != null) {
629                try {
630                    result.setBehavior(
631                            defaultBehavior.value().getDeclaredConstructor().newInstance());
632                } catch (Exception e) {
633                    Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
634                            " could not be instantiated. Did you forget a default constructor?", e);
635                }
636            }
637            result.mBehaviorResolved = true;
638        }
639        return result;
640    }
641
642    private void prepareChildren() {
643        mDependencySortedChildren.clear();
644        mChildDag.clear();
645
646        for (int i = 0, count = getChildCount(); i < count; i++) {
647            final View view = getChildAt(i);
648
649            final LayoutParams lp = getResolvedLayoutParams(view);
650            lp.findAnchorView(this, view);
651
652            mChildDag.addNode(view);
653
654            // Now iterate again over the other children, adding any dependencies to the graph
655            for (int j = 0; j < count; j++) {
656                if (j == i) {
657                    continue;
658                }
659                final View other = getChildAt(j);
660                if (lp.dependsOn(this, view, other)) {
661                    if (!mChildDag.contains(other)) {
662                        // Make sure that the other node is added
663                        mChildDag.addNode(other);
664                    }
665                    // Now add the dependency to the graph
666                    mChildDag.addEdge(other, view);
667                }
668            }
669        }
670
671        // Finally add the sorted graph list to our list
672        mDependencySortedChildren.addAll(mChildDag.getSortedList());
673        // We also need to reverse the result since we want the start of the list to contain
674        // Views which have no dependencies, then dependent views after that
675        Collections.reverse(mDependencySortedChildren);
676    }
677
678    /**
679     * Retrieve the transformed bounding rect of an arbitrary descendant view.
680     * This does not need to be a direct child.
681     *
682     * @param descendant descendant view to reference
683     * @param out rect to set to the bounds of the descendant view
684     */
685    void getDescendantRect(View descendant, Rect out) {
686        ViewGroupUtils.getDescendantRect(this, descendant, out);
687    }
688
689    @Override
690    protected int getSuggestedMinimumWidth() {
691        return Math.max(super.getSuggestedMinimumWidth(), getPaddingLeft() + getPaddingRight());
692    }
693
694    @Override
695    protected int getSuggestedMinimumHeight() {
696        return Math.max(super.getSuggestedMinimumHeight(), getPaddingTop() + getPaddingBottom());
697    }
698
699    /**
700     * Called to measure each individual child view unless a
701     * {@link CoordinatorLayout.Behavior Behavior} is present. The Behavior may choose to delegate
702     * child measurement to this method.
703     *
704     * @param child the child to measure
705     * @param parentWidthMeasureSpec the width requirements for this view
706     * @param widthUsed extra space that has been used up by the parent
707     *        horizontally (possibly by other children of the parent)
708     * @param parentHeightMeasureSpec the height requirements for this view
709     * @param heightUsed extra space that has been used up by the parent
710     *        vertically (possibly by other children of the parent)
711     */
712    public void onMeasureChild(View child, int parentWidthMeasureSpec, int widthUsed,
713            int parentHeightMeasureSpec, int heightUsed) {
714        measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
715                parentHeightMeasureSpec, heightUsed);
716    }
717
718    @Override
719    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
720        prepareChildren();
721        ensurePreDrawListener();
722
723        final int paddingLeft = getPaddingLeft();
724        final int paddingTop = getPaddingTop();
725        final int paddingRight = getPaddingRight();
726        final int paddingBottom = getPaddingBottom();
727        final int layoutDirection = ViewCompat.getLayoutDirection(this);
728        final boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
729        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
730        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
731        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
732        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
733
734        final int widthPadding = paddingLeft + paddingRight;
735        final int heightPadding = paddingTop + paddingBottom;
736        int widthUsed = getSuggestedMinimumWidth();
737        int heightUsed = getSuggestedMinimumHeight();
738        int childState = 0;
739
740        final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);
741
742        final int childCount = mDependencySortedChildren.size();
743        for (int i = 0; i < childCount; i++) {
744            final View child = mDependencySortedChildren.get(i);
745            if (child.getVisibility() == GONE) {
746                // If the child is GONE, skip...
747                continue;
748            }
749
750            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
751
752            int keylineWidthUsed = 0;
753            if (lp.keyline >= 0 && widthMode != MeasureSpec.UNSPECIFIED) {
754                final int keylinePos = getKeyline(lp.keyline);
755                final int keylineGravity = GravityCompat.getAbsoluteGravity(
756                        resolveKeylineGravity(lp.gravity), layoutDirection)
757                        & Gravity.HORIZONTAL_GRAVITY_MASK;
758                if ((keylineGravity == Gravity.LEFT && !isRtl)
759                        || (keylineGravity == Gravity.RIGHT && isRtl)) {
760                    keylineWidthUsed = Math.max(0, widthSize - paddingRight - keylinePos);
761                } else if ((keylineGravity == Gravity.RIGHT && !isRtl)
762                        || (keylineGravity == Gravity.LEFT && isRtl)) {
763                    keylineWidthUsed = Math.max(0, keylinePos - paddingLeft);
764                }
765            }
766
767            int childWidthMeasureSpec = widthMeasureSpec;
768            int childHeightMeasureSpec = heightMeasureSpec;
769            if (applyInsets && !ViewCompat.getFitsSystemWindows(child)) {
770                // We're set to handle insets but this child isn't, so we will measure the
771                // child as if there are no insets
772                final int horizInsets = mLastInsets.getSystemWindowInsetLeft()
773                        + mLastInsets.getSystemWindowInsetRight();
774                final int vertInsets = mLastInsets.getSystemWindowInsetTop()
775                        + mLastInsets.getSystemWindowInsetBottom();
776
777                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
778                        widthSize - horizInsets, widthMode);
779                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
780                        heightSize - vertInsets, heightMode);
781            }
782
783            final Behavior b = lp.getBehavior();
784            if (b == null || !b.onMeasureChild(this, child, childWidthMeasureSpec, keylineWidthUsed,
785                    childHeightMeasureSpec, 0)) {
786                onMeasureChild(child, childWidthMeasureSpec, keylineWidthUsed,
787                        childHeightMeasureSpec, 0);
788            }
789
790            widthUsed = Math.max(widthUsed, widthPadding + child.getMeasuredWidth() +
791                    lp.leftMargin + lp.rightMargin);
792
793            heightUsed = Math.max(heightUsed, heightPadding + child.getMeasuredHeight() +
794                    lp.topMargin + lp.bottomMargin);
795            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
796        }
797
798        final int width = View.resolveSizeAndState(widthUsed, widthMeasureSpec,
799                childState & View.MEASURED_STATE_MASK);
800        final int height = View.resolveSizeAndState(heightUsed, heightMeasureSpec,
801                childState << View.MEASURED_HEIGHT_STATE_SHIFT);
802        setMeasuredDimension(width, height);
803    }
804
805    private WindowInsetsCompat dispatchApplyWindowInsetsToBehaviors(WindowInsetsCompat insets) {
806        if (insets.isConsumed()) {
807            return insets;
808        }
809
810        for (int i = 0, z = getChildCount(); i < z; i++) {
811            final View child = getChildAt(i);
812            if (ViewCompat.getFitsSystemWindows(child)) {
813                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
814                final Behavior b = lp.getBehavior();
815
816                if (b != null) {
817                    // If the view has a behavior, let it try first
818                    insets = b.onApplyWindowInsets(this, child, insets);
819                    if (insets.isConsumed()) {
820                        // If it consumed the insets, break
821                        break;
822                    }
823                }
824            }
825        }
826
827        return insets;
828    }
829
830    /**
831     * Called to lay out each individual child view unless a
832     * {@link CoordinatorLayout.Behavior Behavior} is present. The Behavior may choose to
833     * delegate child measurement to this method.
834     *
835     * @param child child view to lay out
836     * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
837     *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
838     *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
839     */
840    public void onLayoutChild(View child, int layoutDirection) {
841        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
842        if (lp.checkAnchorChanged()) {
843            throw new IllegalStateException("An anchor may not be changed after CoordinatorLayout"
844                    + " measurement begins before layout is complete.");
845        }
846        if (lp.mAnchorView != null) {
847            layoutChildWithAnchor(child, lp.mAnchorView, layoutDirection);
848        } else if (lp.keyline >= 0) {
849            layoutChildWithKeyline(child, lp.keyline, layoutDirection);
850        } else {
851            layoutChild(child, layoutDirection);
852        }
853    }
854
855    @Override
856    protected void onLayout(boolean changed, int l, int t, int r, int b) {
857        final int layoutDirection = ViewCompat.getLayoutDirection(this);
858        final int childCount = mDependencySortedChildren.size();
859        for (int i = 0; i < childCount; i++) {
860            final View child = mDependencySortedChildren.get(i);
861            if (child.getVisibility() == GONE) {
862                // If the child is GONE, skip...
863                continue;
864            }
865
866            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
867            final Behavior behavior = lp.getBehavior();
868
869            if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
870                onLayoutChild(child, layoutDirection);
871            }
872        }
873    }
874
875    @Override
876    public void onDraw(Canvas c) {
877        super.onDraw(c);
878        if (mDrawStatusBarBackground && mStatusBarBackground != null) {
879            final int inset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
880            if (inset > 0) {
881                mStatusBarBackground.setBounds(0, 0, getWidth(), inset);
882                mStatusBarBackground.draw(c);
883            }
884        }
885    }
886
887    @Override
888    public void setFitsSystemWindows(boolean fitSystemWindows) {
889        super.setFitsSystemWindows(fitSystemWindows);
890        setupForInsets();
891    }
892
893    /**
894     * Mark the last known child position rect for the given child view.
895     * This will be used when checking if a child view's position has changed between frames.
896     * The rect used here should be one returned by
897     * {@link #getChildRect(android.view.View, boolean, android.graphics.Rect)}, with translation
898     * disabled.
899     *
900     * @param child child view to set for
901     * @param r rect to set
902     */
903    void recordLastChildRect(View child, Rect r) {
904        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
905        lp.setLastChildRect(r);
906    }
907
908    /**
909     * Get the last known child rect recorded by
910     * {@link #recordLastChildRect(android.view.View, android.graphics.Rect)}.
911     *
912     * @param child child view to retrieve from
913     * @param out rect to set to the outpur values
914     */
915    void getLastChildRect(View child, Rect out) {
916        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
917        out.set(lp.getLastChildRect());
918    }
919
920    /**
921     * Get the position rect for the given child. If the child has currently requested layout
922     * or has a visibility of GONE.
923     *
924     * @param child child view to check
925     * @param transform true to include transformation in the output rect, false to
926     *                        only account for the base position
927     * @param out rect to set to the output values
928     */
929    void getChildRect(View child, boolean transform, Rect out) {
930        if (child.isLayoutRequested() || child.getVisibility() == View.GONE) {
931            out.setEmpty();
932            return;
933        }
934        if (transform) {
935            getDescendantRect(child, out);
936        } else {
937            out.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
938        }
939    }
940
941    private void getDesiredAnchoredChildRectWithoutConstraints(View child, int layoutDirection,
942            Rect anchorRect, Rect out, LayoutParams lp, int childWidth, int childHeight) {
943        final int absGravity = GravityCompat.getAbsoluteGravity(
944                resolveAnchoredChildGravity(lp.gravity), layoutDirection);
945        final int absAnchorGravity = GravityCompat.getAbsoluteGravity(
946                resolveGravity(lp.anchorGravity),
947                layoutDirection);
948
949        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
950        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
951        final int anchorHgrav = absAnchorGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
952        final int anchorVgrav = absAnchorGravity & Gravity.VERTICAL_GRAVITY_MASK;
953
954        int left;
955        int top;
956
957        // Align to the anchor. This puts us in an assumed right/bottom child view gravity.
958        // If this is not the case we will subtract out the appropriate portion of
959        // the child size below.
960        switch (anchorHgrav) {
961            default:
962            case Gravity.LEFT:
963                left = anchorRect.left;
964                break;
965            case Gravity.RIGHT:
966                left = anchorRect.right;
967                break;
968            case Gravity.CENTER_HORIZONTAL:
969                left = anchorRect.left + anchorRect.width() / 2;
970                break;
971        }
972
973        switch (anchorVgrav) {
974            default:
975            case Gravity.TOP:
976                top = anchorRect.top;
977                break;
978            case Gravity.BOTTOM:
979                top = anchorRect.bottom;
980                break;
981            case Gravity.CENTER_VERTICAL:
982                top = anchorRect.top + anchorRect.height() / 2;
983                break;
984        }
985
986        // Offset by the child view's gravity itself. The above assumed right/bottom gravity.
987        switch (hgrav) {
988            default:
989            case Gravity.LEFT:
990                left -= childWidth;
991                break;
992            case Gravity.RIGHT:
993                // Do nothing, we're already in position.
994                break;
995            case Gravity.CENTER_HORIZONTAL:
996                left -= childWidth / 2;
997                break;
998        }
999
1000        switch (vgrav) {
1001            default:
1002            case Gravity.TOP:
1003                top -= childHeight;
1004                break;
1005            case Gravity.BOTTOM:
1006                // Do nothing, we're already in position.
1007                break;
1008            case Gravity.CENTER_VERTICAL:
1009                top -= childHeight / 2;
1010                break;
1011        }
1012
1013        out.set(left, top, left + childWidth, top + childHeight);
1014    }
1015
1016    private void constrainChildRect(LayoutParams lp, Rect out, int childWidth, int childHeight) {
1017        final int width = getWidth();
1018        final int height = getHeight();
1019
1020        // Obey margins and padding
1021        int left = Math.max(getPaddingLeft() + lp.leftMargin,
1022                Math.min(out.left,
1023                        width - getPaddingRight() - childWidth - lp.rightMargin));
1024        int top = Math.max(getPaddingTop() + lp.topMargin,
1025                Math.min(out.top,
1026                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
1027
1028        out.set(left, top, left + childWidth, top + childHeight);
1029    }
1030
1031    /**
1032     * Calculate the desired child rect relative to an anchor rect, respecting both
1033     * gravity and anchorGravity.
1034     *
1035     * @param child child view to calculate a rect for
1036     * @param layoutDirection the desired layout direction for the CoordinatorLayout
1037     * @param anchorRect rect in CoordinatorLayout coordinates of the anchor view area
1038     * @param out rect to set to the output values
1039     */
1040    void getDesiredAnchoredChildRect(View child, int layoutDirection, Rect anchorRect, Rect out) {
1041        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1042        final int childWidth = child.getMeasuredWidth();
1043        final int childHeight = child.getMeasuredHeight();
1044        getDesiredAnchoredChildRectWithoutConstraints(child, layoutDirection, anchorRect, out, lp,
1045                childWidth, childHeight);
1046        constrainChildRect(lp, out, childWidth, childHeight);
1047    }
1048
1049    /**
1050     * CORE ASSUMPTION: anchor has been laid out by the time this is called for a given child view.
1051     *
1052     * @param child child to lay out
1053     * @param anchor view to anchor child relative to; already laid out.
1054     * @param layoutDirection ViewCompat constant for layout direction
1055     */
1056    private void layoutChildWithAnchor(View child, View anchor, int layoutDirection) {
1057        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1058
1059        final Rect anchorRect = acquireTempRect();
1060        final Rect childRect = acquireTempRect();
1061        try {
1062            getDescendantRect(anchor, anchorRect);
1063            getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, childRect);
1064            child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
1065        } finally {
1066            releaseTempRect(anchorRect);
1067            releaseTempRect(childRect);
1068        }
1069    }
1070
1071    /**
1072     * Lay out a child view with respect to a keyline.
1073     *
1074     * <p>The keyline represents a horizontal offset from the unpadded starting edge of
1075     * the CoordinatorLayout. The child's gravity will affect how it is positioned with
1076     * respect to the keyline.</p>
1077     *
1078     * @param child child to lay out
1079     * @param keyline offset from the starting edge in pixels of the keyline to align with
1080     * @param layoutDirection ViewCompat constant for layout direction
1081     */
1082    private void layoutChildWithKeyline(View child, int keyline, int layoutDirection) {
1083        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1084        final int absGravity = GravityCompat.getAbsoluteGravity(
1085                resolveKeylineGravity(lp.gravity), layoutDirection);
1086
1087        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1088        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
1089        final int width = getWidth();
1090        final int height = getHeight();
1091        final int childWidth = child.getMeasuredWidth();
1092        final int childHeight = child.getMeasuredHeight();
1093
1094        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
1095            keyline = width - keyline;
1096        }
1097
1098        int left = getKeyline(keyline) - childWidth;
1099        int top = 0;
1100
1101        switch (hgrav) {
1102            default:
1103            case Gravity.LEFT:
1104                // Nothing to do.
1105                break;
1106            case Gravity.RIGHT:
1107                left += childWidth;
1108                break;
1109            case Gravity.CENTER_HORIZONTAL:
1110                left += childWidth / 2;
1111                break;
1112        }
1113
1114        switch (vgrav) {
1115            default:
1116            case Gravity.TOP:
1117                // Do nothing, we're already in position.
1118                break;
1119            case Gravity.BOTTOM:
1120                top += childHeight;
1121                break;
1122            case Gravity.CENTER_VERTICAL:
1123                top += childHeight / 2;
1124                break;
1125        }
1126
1127        // Obey margins and padding
1128        left = Math.max(getPaddingLeft() + lp.leftMargin,
1129                Math.min(left,
1130                        width - getPaddingRight() - childWidth - lp.rightMargin));
1131        top = Math.max(getPaddingTop() + lp.topMargin,
1132                Math.min(top,
1133                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
1134
1135        child.layout(left, top, left + childWidth, top + childHeight);
1136    }
1137
1138    /**
1139     * Lay out a child view with no special handling. This will position the child as
1140     * if it were within a FrameLayout or similar simple frame.
1141     *
1142     * @param child child view to lay out
1143     * @param layoutDirection ViewCompat constant for the desired layout direction
1144     */
1145    private void layoutChild(View child, int layoutDirection) {
1146        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1147        final Rect parent = acquireTempRect();
1148        parent.set(getPaddingLeft() + lp.leftMargin,
1149                getPaddingTop() + lp.topMargin,
1150                getWidth() - getPaddingRight() - lp.rightMargin,
1151                getHeight() - getPaddingBottom() - lp.bottomMargin);
1152
1153        if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this)
1154                && !ViewCompat.getFitsSystemWindows(child)) {
1155            // If we're set to handle insets but this child isn't, then it has been measured as
1156            // if there are no insets. We need to lay it out to match.
1157            parent.left += mLastInsets.getSystemWindowInsetLeft();
1158            parent.top += mLastInsets.getSystemWindowInsetTop();
1159            parent.right -= mLastInsets.getSystemWindowInsetRight();
1160            parent.bottom -= mLastInsets.getSystemWindowInsetBottom();
1161        }
1162
1163        final Rect out = acquireTempRect();
1164        GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
1165                child.getMeasuredHeight(), parent, out, layoutDirection);
1166        child.layout(out.left, out.top, out.right, out.bottom);
1167
1168        releaseTempRect(parent);
1169        releaseTempRect(out);
1170    }
1171
1172    /**
1173     * Return the given gravity value, but if either or both of the axes doesn't have any gravity
1174     * specified, the default value (start or top) is specified. This should be used for children
1175     * that are not anchored to another view or a keyline.
1176     */
1177    private static int resolveGravity(int gravity) {
1178        if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
1179            gravity |= GravityCompat.START;
1180        }
1181        if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
1182            gravity |= Gravity.TOP;
1183        }
1184        return gravity;
1185    }
1186
1187    /**
1188     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
1189     * This should be used for children that are positioned relative to a keyline.
1190     */
1191    private static int resolveKeylineGravity(int gravity) {
1192        return gravity == Gravity.NO_GRAVITY ? GravityCompat.END | Gravity.TOP : gravity;
1193    }
1194
1195    /**
1196     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
1197     * This should be used for children that are anchored to another view.
1198     */
1199    private static int resolveAnchoredChildGravity(int gravity) {
1200        return gravity == Gravity.NO_GRAVITY ? Gravity.CENTER : gravity;
1201    }
1202
1203    @Override
1204    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
1205        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1206        if (lp.mBehavior != null) {
1207            final float scrimAlpha = lp.mBehavior.getScrimOpacity(this, child);
1208            if (scrimAlpha > 0f) {
1209                if (mScrimPaint == null) {
1210                    mScrimPaint = new Paint();
1211                }
1212                mScrimPaint.setColor(lp.mBehavior.getScrimColor(this, child));
1213                mScrimPaint.setAlpha(MathUtils.clamp(Math.round(255 * scrimAlpha), 0, 255));
1214
1215                final int saved = canvas.save();
1216                if (child.isOpaque()) {
1217                    // If the child is opaque, there is no need to draw behind it so we'll inverse
1218                    // clip the canvas
1219                    canvas.clipRect(child.getLeft(), child.getTop(), child.getRight(),
1220                            child.getBottom(), Region.Op.DIFFERENCE);
1221                }
1222                // Now draw the rectangle for the scrim
1223                canvas.drawRect(getPaddingLeft(), getPaddingTop(),
1224                        getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(),
1225                        mScrimPaint);
1226                canvas.restoreToCount(saved);
1227            }
1228        }
1229        return super.drawChild(canvas, child, drawingTime);
1230    }
1231
1232    /**
1233     * Dispatch any dependent view changes to the relevant {@link Behavior} instances.
1234     *
1235     * Usually run as part of the pre-draw step when at least one child view has a reported
1236     * dependency on another view. This allows CoordinatorLayout to account for layout
1237     * changes and animations that occur outside of the normal layout pass.
1238     *
1239     * It can also be ran as part of the nested scrolling dispatch to ensure that any offsetting
1240     * is completed within the correct coordinate window.
1241     *
1242     * The offsetting behavior implemented here does not store the computed offset in
1243     * the LayoutParams; instead it expects that the layout process will always reconstruct
1244     * the proper positioning.
1245     *
1246     * @param type the type of event which has caused this call
1247     */
1248    final void onChildViewsChanged(@DispatchChangeEvent final int type) {
1249        final int layoutDirection = ViewCompat.getLayoutDirection(this);
1250        final int childCount = mDependencySortedChildren.size();
1251        final Rect inset = acquireTempRect();
1252        final Rect drawRect = acquireTempRect();
1253        final Rect lastDrawRect = acquireTempRect();
1254
1255        for (int i = 0; i < childCount; i++) {
1256            final View child = mDependencySortedChildren.get(i);
1257            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1258            if (type == EVENT_PRE_DRAW && child.getVisibility() == View.GONE) {
1259                // Do not try to update GONE child views in pre draw updates.
1260                continue;
1261            }
1262
1263            // Check child views before for anchor
1264            for (int j = 0; j < i; j++) {
1265                final View checkChild = mDependencySortedChildren.get(j);
1266
1267                if (lp.mAnchorDirectChild == checkChild) {
1268                    offsetChildToAnchor(child, layoutDirection);
1269                }
1270            }
1271
1272            // Get the current draw rect of the view
1273            getChildRect(child, true, drawRect);
1274
1275            // Accumulate inset sizes
1276            if (lp.insetEdge != Gravity.NO_GRAVITY && !drawRect.isEmpty()) {
1277                final int absInsetEdge = GravityCompat.getAbsoluteGravity(
1278                        lp.insetEdge, layoutDirection);
1279                switch (absInsetEdge & Gravity.VERTICAL_GRAVITY_MASK) {
1280                    case Gravity.TOP:
1281                        inset.top = Math.max(inset.top, drawRect.bottom);
1282                        break;
1283                    case Gravity.BOTTOM:
1284                        inset.bottom = Math.max(inset.bottom, getHeight() - drawRect.top);
1285                        break;
1286                }
1287                switch (absInsetEdge & Gravity.HORIZONTAL_GRAVITY_MASK) {
1288                    case Gravity.LEFT:
1289                        inset.left = Math.max(inset.left, drawRect.right);
1290                        break;
1291                    case Gravity.RIGHT:
1292                        inset.right = Math.max(inset.right, getWidth() - drawRect.left);
1293                        break;
1294                }
1295            }
1296
1297            // Dodge inset edges if necessary
1298            if (lp.dodgeInsetEdges != Gravity.NO_GRAVITY && child.getVisibility() == View.VISIBLE) {
1299                offsetChildByInset(child, inset, layoutDirection);
1300            }
1301
1302            if (type != EVENT_VIEW_REMOVED) {
1303                // Did it change? if not continue
1304                getLastChildRect(child, lastDrawRect);
1305                if (lastDrawRect.equals(drawRect)) {
1306                    continue;
1307                }
1308                recordLastChildRect(child, drawRect);
1309            }
1310
1311            // Update any behavior-dependent views for the change
1312            for (int j = i + 1; j < childCount; j++) {
1313                final View checkChild = mDependencySortedChildren.get(j);
1314                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
1315                final Behavior b = checkLp.getBehavior();
1316
1317                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
1318                    if (type == EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {
1319                        // If this is from a pre-draw and we have already been changed
1320                        // from a nested scroll, skip the dispatch and reset the flag
1321                        checkLp.resetChangedAfterNestedScroll();
1322                        continue;
1323                    }
1324
1325                    final boolean handled;
1326                    switch (type) {
1327                        case EVENT_VIEW_REMOVED:
1328                            // EVENT_VIEW_REMOVED means that we need to dispatch
1329                            // onDependentViewRemoved() instead
1330                            b.onDependentViewRemoved(this, checkChild, child);
1331                            handled = true;
1332                            break;
1333                        default:
1334                            // Otherwise we dispatch onDependentViewChanged()
1335                            handled = b.onDependentViewChanged(this, checkChild, child);
1336                            break;
1337                    }
1338
1339                    if (type == EVENT_NESTED_SCROLL) {
1340                        // If this is from a nested scroll, set the flag so that we may skip
1341                        // any resulting onPreDraw dispatch (if needed)
1342                        checkLp.setChangedAfterNestedScroll(handled);
1343                    }
1344                }
1345            }
1346        }
1347
1348        releaseTempRect(inset);
1349        releaseTempRect(drawRect);
1350        releaseTempRect(lastDrawRect);
1351    }
1352
1353    private void offsetChildByInset(final View child, final Rect inset, final int layoutDirection) {
1354        if (!ViewCompat.isLaidOut(child)) {
1355            // The view has not been laid out yet, so we can't obtain its bounds.
1356            return;
1357        }
1358
1359        if (child.getWidth() <= 0 || child.getHeight() <= 0) {
1360            // Bounds are empty so there is nothing to dodge against, skip...
1361            return;
1362        }
1363
1364        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1365        final Behavior behavior = lp.getBehavior();
1366        final Rect dodgeRect = acquireTempRect();
1367        final Rect bounds = acquireTempRect();
1368        bounds.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
1369
1370        if (behavior != null && behavior.getInsetDodgeRect(this, child, dodgeRect)) {
1371            // Make sure that the rect is within the view's bounds
1372            if (!bounds.contains(dodgeRect)) {
1373                throw new IllegalArgumentException("Rect should be within the child's bounds."
1374                        + " Rect:" + dodgeRect.toShortString()
1375                        + " | Bounds:" + bounds.toShortString());
1376            }
1377        } else {
1378            dodgeRect.set(bounds);
1379        }
1380
1381        // We can release the bounds rect now
1382        releaseTempRect(bounds);
1383
1384        if (dodgeRect.isEmpty()) {
1385            // Rect is empty so there is nothing to dodge against, skip...
1386            releaseTempRect(dodgeRect);
1387            return;
1388        }
1389
1390        final int absDodgeInsetEdges = GravityCompat.getAbsoluteGravity(lp.dodgeInsetEdges,
1391                layoutDirection);
1392
1393        boolean offsetY = false;
1394        if ((absDodgeInsetEdges & Gravity.TOP) == Gravity.TOP) {
1395            int distance = dodgeRect.top - lp.topMargin - lp.mInsetOffsetY;
1396            if (distance < inset.top) {
1397                setInsetOffsetY(child, inset.top - distance);
1398                offsetY = true;
1399            }
1400        }
1401        if ((absDodgeInsetEdges & Gravity.BOTTOM) == Gravity.BOTTOM) {
1402            int distance = getHeight() - dodgeRect.bottom - lp.bottomMargin + lp.mInsetOffsetY;
1403            if (distance < inset.bottom) {
1404                setInsetOffsetY(child, distance - inset.bottom);
1405                offsetY = true;
1406            }
1407        }
1408        if (!offsetY) {
1409            setInsetOffsetY(child, 0);
1410        }
1411
1412        boolean offsetX = false;
1413        if ((absDodgeInsetEdges & Gravity.LEFT) == Gravity.LEFT) {
1414            int distance = dodgeRect.left - lp.leftMargin - lp.mInsetOffsetX;
1415            if (distance < inset.left) {
1416                setInsetOffsetX(child, inset.left - distance);
1417                offsetX = true;
1418            }
1419        }
1420        if ((absDodgeInsetEdges & Gravity.RIGHT) == Gravity.RIGHT) {
1421            int distance = getWidth() - dodgeRect.right - lp.rightMargin + lp.mInsetOffsetX;
1422            if (distance < inset.right) {
1423                setInsetOffsetX(child, distance - inset.right);
1424                offsetX = true;
1425            }
1426        }
1427        if (!offsetX) {
1428            setInsetOffsetX(child, 0);
1429        }
1430
1431        releaseTempRect(dodgeRect);
1432    }
1433
1434    private void setInsetOffsetX(View child, int offsetX) {
1435        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1436        if (lp.mInsetOffsetX != offsetX) {
1437            final int dx = offsetX - lp.mInsetOffsetX;
1438            ViewCompat.offsetLeftAndRight(child, dx);
1439            lp.mInsetOffsetX = offsetX;
1440        }
1441    }
1442
1443    private void setInsetOffsetY(View child, int offsetY) {
1444        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1445        if (lp.mInsetOffsetY != offsetY) {
1446            final int dy = offsetY - lp.mInsetOffsetY;
1447            ViewCompat.offsetTopAndBottom(child, dy);
1448            lp.mInsetOffsetY = offsetY;
1449        }
1450    }
1451
1452    /**
1453     * Allows the caller to manually dispatch
1454     * {@link Behavior#onDependentViewChanged(CoordinatorLayout, View, View)} to the associated
1455     * {@link Behavior} instances of views which depend on the provided {@link View}.
1456     *
1457     * <p>You should not normally need to call this method as the it will be automatically done
1458     * when the view has changed.
1459     *
1460     * @param view the View to find dependents of to dispatch the call.
1461     */
1462    public void dispatchDependentViewsChanged(View view) {
1463        final List<View> dependents = mChildDag.getIncomingEdges(view);
1464        if (dependents != null && !dependents.isEmpty()) {
1465            for (int i = 0; i < dependents.size(); i++) {
1466                final View child = dependents.get(i);
1467                CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)
1468                        child.getLayoutParams();
1469                CoordinatorLayout.Behavior b = lp.getBehavior();
1470                if (b != null) {
1471                    b.onDependentViewChanged(this, child, view);
1472                }
1473            }
1474        }
1475    }
1476
1477    /**
1478     * Returns the list of views which the provided view depends on. Do not store this list as its
1479     * contents may not be valid beyond the caller.
1480     *
1481     * @param child the view to find dependencies for.
1482     *
1483     * @return the list of views which {@code child} depends on.
1484     */
1485    @NonNull
1486    public List<View> getDependencies(@NonNull View child) {
1487        final List<View> dependencies = mChildDag.getOutgoingEdges(child);
1488        mTempDependenciesList.clear();
1489        if (dependencies != null) {
1490            mTempDependenciesList.addAll(dependencies);
1491        }
1492        return mTempDependenciesList;
1493    }
1494
1495    /**
1496     * Returns the list of views which depend on the provided view. Do not store this list as its
1497     * contents may not be valid beyond the caller.
1498     *
1499     * @param child the view to find dependents of.
1500     *
1501     * @return the list of views which depend on {@code child}.
1502     */
1503    @NonNull
1504    public List<View> getDependents(@NonNull View child) {
1505        final List<View> edges = mChildDag.getIncomingEdges(child);
1506        mTempDependenciesList.clear();
1507        if (edges != null) {
1508            mTempDependenciesList.addAll(edges);
1509        }
1510        return mTempDependenciesList;
1511    }
1512
1513    @VisibleForTesting
1514    final List<View> getDependencySortedChildren() {
1515        prepareChildren();
1516        return Collections.unmodifiableList(mDependencySortedChildren);
1517    }
1518
1519    /**
1520     * Add or remove the pre-draw listener as necessary.
1521     */
1522    void ensurePreDrawListener() {
1523        boolean hasDependencies = false;
1524        final int childCount = getChildCount();
1525        for (int i = 0; i < childCount; i++) {
1526            final View child = getChildAt(i);
1527            if (hasDependencies(child)) {
1528                hasDependencies = true;
1529                break;
1530            }
1531        }
1532
1533        if (hasDependencies != mNeedsPreDrawListener) {
1534            if (hasDependencies) {
1535                addPreDrawListener();
1536            } else {
1537                removePreDrawListener();
1538            }
1539        }
1540    }
1541
1542    /**
1543     * Check if the given child has any layout dependencies on other child views.
1544     */
1545    private boolean hasDependencies(View child) {
1546        return mChildDag.hasOutgoingEdges(child);
1547    }
1548
1549    /**
1550     * Add the pre-draw listener if we're attached to a window and mark that we currently
1551     * need it when attached.
1552     */
1553    void addPreDrawListener() {
1554        if (mIsAttachedToWindow) {
1555            // Add the listener
1556            if (mOnPreDrawListener == null) {
1557                mOnPreDrawListener = new OnPreDrawListener();
1558            }
1559            final ViewTreeObserver vto = getViewTreeObserver();
1560            vto.addOnPreDrawListener(mOnPreDrawListener);
1561        }
1562
1563        // Record that we need the listener regardless of whether or not we're attached.
1564        // We'll add the real listener when we become attached.
1565        mNeedsPreDrawListener = true;
1566    }
1567
1568    /**
1569     * Remove the pre-draw listener if we're attached to a window and mark that we currently
1570     * do not need it when attached.
1571     */
1572    void removePreDrawListener() {
1573        if (mIsAttachedToWindow) {
1574            if (mOnPreDrawListener != null) {
1575                final ViewTreeObserver vto = getViewTreeObserver();
1576                vto.removeOnPreDrawListener(mOnPreDrawListener);
1577            }
1578        }
1579        mNeedsPreDrawListener = false;
1580    }
1581
1582    /**
1583     * Adjust the child left, top, right, bottom rect to the correct anchor view position,
1584     * respecting gravity and anchor gravity.
1585     *
1586     * Note that child translation properties are ignored in this process, allowing children
1587     * to be animated away from their anchor. However, if the anchor view is animated,
1588     * the child will be offset to match the anchor's translated position.
1589     */
1590    void offsetChildToAnchor(View child, int layoutDirection) {
1591        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1592        if (lp.mAnchorView != null) {
1593            final Rect anchorRect = acquireTempRect();
1594            final Rect childRect = acquireTempRect();
1595            final Rect desiredChildRect = acquireTempRect();
1596
1597            getDescendantRect(lp.mAnchorView, anchorRect);
1598            getChildRect(child, false, childRect);
1599
1600            int childWidth = child.getMeasuredWidth();
1601            int childHeight = child.getMeasuredHeight();
1602            getDesiredAnchoredChildRectWithoutConstraints(child, layoutDirection, anchorRect,
1603                    desiredChildRect, lp, childWidth, childHeight);
1604            boolean changed = desiredChildRect.left != childRect.left ||
1605                    desiredChildRect.top != childRect.top;
1606            constrainChildRect(lp, desiredChildRect, childWidth, childHeight);
1607
1608            final int dx = desiredChildRect.left - childRect.left;
1609            final int dy = desiredChildRect.top - childRect.top;
1610
1611            if (dx != 0) {
1612                ViewCompat.offsetLeftAndRight(child, dx);
1613            }
1614            if (dy != 0) {
1615                ViewCompat.offsetTopAndBottom(child, dy);
1616            }
1617
1618            if (changed) {
1619                // If we have needed to move, make sure to notify the child's Behavior
1620                final Behavior b = lp.getBehavior();
1621                if (b != null) {
1622                    b.onDependentViewChanged(this, child, lp.mAnchorView);
1623                }
1624            }
1625
1626            releaseTempRect(anchorRect);
1627            releaseTempRect(childRect);
1628            releaseTempRect(desiredChildRect);
1629        }
1630    }
1631
1632    /**
1633     * Check if a given point in the CoordinatorLayout's coordinates are within the view bounds
1634     * of the given direct child view.
1635     *
1636     * @param child child view to test
1637     * @param x X coordinate to test, in the CoordinatorLayout's coordinate system
1638     * @param y Y coordinate to test, in the CoordinatorLayout's coordinate system
1639     * @return true if the point is within the child view's bounds, false otherwise
1640     */
1641    public boolean isPointInChildBounds(View child, int x, int y) {
1642        final Rect r = acquireTempRect();
1643        getDescendantRect(child, r);
1644        try {
1645            return r.contains(x, y);
1646        } finally {
1647            releaseTempRect(r);
1648        }
1649    }
1650
1651    /**
1652     * Check whether two views overlap each other. The views need to be descendants of this
1653     * {@link CoordinatorLayout} in the view hierarchy.
1654     *
1655     * @param first first child view to test
1656     * @param second second child view to test
1657     * @return true if both views are visible and overlap each other
1658     */
1659    public boolean doViewsOverlap(View first, View second) {
1660        if (first.getVisibility() == VISIBLE && second.getVisibility() == VISIBLE) {
1661            final Rect firstRect = acquireTempRect();
1662            getChildRect(first, first.getParent() != this, firstRect);
1663            final Rect secondRect = acquireTempRect();
1664            getChildRect(second, second.getParent() != this, secondRect);
1665            try {
1666                return !(firstRect.left > secondRect.right || firstRect.top > secondRect.bottom
1667                        || firstRect.right < secondRect.left || firstRect.bottom < secondRect.top);
1668            } finally {
1669                releaseTempRect(firstRect);
1670                releaseTempRect(secondRect);
1671            }
1672        }
1673        return false;
1674    }
1675
1676    @Override
1677    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1678        return new LayoutParams(getContext(), attrs);
1679    }
1680
1681    @Override
1682    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1683        if (p instanceof LayoutParams) {
1684            return new LayoutParams((LayoutParams) p);
1685        } else if (p instanceof MarginLayoutParams) {
1686            return new LayoutParams((MarginLayoutParams) p);
1687        }
1688        return new LayoutParams(p);
1689    }
1690
1691    @Override
1692    protected LayoutParams generateDefaultLayoutParams() {
1693        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1694    }
1695
1696    @Override
1697    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1698        return p instanceof LayoutParams && super.checkLayoutParams(p);
1699    }
1700
1701    @Override
1702    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
1703        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
1704    }
1705
1706    @Override
1707    public boolean onStartNestedScroll(View child, View target, int axes, int type) {
1708        boolean handled = false;
1709
1710        final int childCount = getChildCount();
1711        for (int i = 0; i < childCount; i++) {
1712            final View view = getChildAt(i);
1713            if (view.getVisibility() == View.GONE) {
1714                // If it's GONE, don't dispatch
1715                continue;
1716            }
1717            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1718            final Behavior viewBehavior = lp.getBehavior();
1719            if (viewBehavior != null) {
1720                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
1721                        target, axes, type);
1722                handled |= accepted;
1723                lp.setNestedScrollAccepted(type, accepted);
1724            } else {
1725                lp.setNestedScrollAccepted(type, false);
1726            }
1727        }
1728        return handled;
1729    }
1730
1731    @Override
1732    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
1733        onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
1734    }
1735
1736    @Override
1737    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes, int type) {
1738        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes, type);
1739        mNestedScrollingTarget = target;
1740
1741        final int childCount = getChildCount();
1742        for (int i = 0; i < childCount; i++) {
1743            final View view = getChildAt(i);
1744            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1745            if (!lp.isNestedScrollAccepted(type)) {
1746                continue;
1747            }
1748
1749            final Behavior viewBehavior = lp.getBehavior();
1750            if (viewBehavior != null) {
1751                viewBehavior.onNestedScrollAccepted(this, view, child, target,
1752                        nestedScrollAxes, type);
1753            }
1754        }
1755    }
1756
1757    @Override
1758    public void onStopNestedScroll(View target) {
1759        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
1760    }
1761
1762    @Override
1763    public void onStopNestedScroll(View target, int type) {
1764        mNestedScrollingParentHelper.onStopNestedScroll(target, type);
1765
1766        final int childCount = getChildCount();
1767        for (int i = 0; i < childCount; i++) {
1768            final View view = getChildAt(i);
1769            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1770            if (!lp.isNestedScrollAccepted(type)) {
1771                continue;
1772            }
1773
1774            final Behavior viewBehavior = lp.getBehavior();
1775            if (viewBehavior != null) {
1776                viewBehavior.onStopNestedScroll(this, view, target, type);
1777            }
1778            lp.resetNestedScroll(type);
1779            lp.resetChangedAfterNestedScroll();
1780        }
1781        mNestedScrollingTarget = null;
1782    }
1783
1784    @Override
1785    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
1786            int dxUnconsumed, int dyUnconsumed) {
1787        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
1788                ViewCompat.TYPE_TOUCH);
1789    }
1790
1791    @Override
1792    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
1793            int dxUnconsumed, int dyUnconsumed, int type) {
1794        final int childCount = getChildCount();
1795        boolean accepted = false;
1796
1797        for (int i = 0; i < childCount; i++) {
1798            final View view = getChildAt(i);
1799            if (view.getVisibility() == GONE) {
1800                // If the child is GONE, skip...
1801                continue;
1802            }
1803
1804            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1805            if (!lp.isNestedScrollAccepted(type)) {
1806                continue;
1807            }
1808
1809            final Behavior viewBehavior = lp.getBehavior();
1810            if (viewBehavior != null) {
1811                viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed,
1812                        dxUnconsumed, dyUnconsumed, type);
1813                accepted = true;
1814            }
1815        }
1816
1817        if (accepted) {
1818            onChildViewsChanged(EVENT_NESTED_SCROLL);
1819        }
1820    }
1821
1822    @Override
1823    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
1824        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
1825    }
1826
1827    @Override
1828    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int  type) {
1829        int xConsumed = 0;
1830        int yConsumed = 0;
1831        boolean accepted = false;
1832
1833        final int childCount = getChildCount();
1834        for (int i = 0; i < childCount; i++) {
1835            final View view = getChildAt(i);
1836            if (view.getVisibility() == GONE) {
1837                // If the child is GONE, skip...
1838                continue;
1839            }
1840
1841            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1842            if (!lp.isNestedScrollAccepted(type)) {
1843                continue;
1844            }
1845
1846            final Behavior viewBehavior = lp.getBehavior();
1847            if (viewBehavior != null) {
1848                mTempIntPair[0] = mTempIntPair[1] = 0;
1849                viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair, type);
1850
1851                xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
1852                        : Math.min(xConsumed, mTempIntPair[0]);
1853                yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
1854                        : Math.min(yConsumed, mTempIntPair[1]);
1855
1856                accepted = true;
1857            }
1858        }
1859
1860        consumed[0] = xConsumed;
1861        consumed[1] = yConsumed;
1862
1863        if (accepted) {
1864            onChildViewsChanged(EVENT_NESTED_SCROLL);
1865        }
1866    }
1867
1868    @Override
1869    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
1870        boolean handled = false;
1871
1872        final int childCount = getChildCount();
1873        for (int i = 0; i < childCount; i++) {
1874            final View view = getChildAt(i);
1875            if (view.getVisibility() == GONE) {
1876                // If the child is GONE, skip...
1877                continue;
1878            }
1879
1880            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1881            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
1882                continue;
1883            }
1884
1885            final Behavior viewBehavior = lp.getBehavior();
1886            if (viewBehavior != null) {
1887                handled |= viewBehavior.onNestedFling(this, view, target, velocityX, velocityY,
1888                        consumed);
1889            }
1890        }
1891        if (handled) {
1892            onChildViewsChanged(EVENT_NESTED_SCROLL);
1893        }
1894        return handled;
1895    }
1896
1897    @Override
1898    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
1899        boolean handled = false;
1900
1901        final int childCount = getChildCount();
1902        for (int i = 0; i < childCount; i++) {
1903            final View view = getChildAt(i);
1904            if (view.getVisibility() == GONE) {
1905                // If the child is GONE, skip...
1906                continue;
1907            }
1908
1909            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
1910            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
1911                continue;
1912            }
1913
1914            final Behavior viewBehavior = lp.getBehavior();
1915            if (viewBehavior != null) {
1916                handled |= viewBehavior.onNestedPreFling(this, view, target, velocityX, velocityY);
1917            }
1918        }
1919        return handled;
1920    }
1921
1922    @Override
1923    public int getNestedScrollAxes() {
1924        return mNestedScrollingParentHelper.getNestedScrollAxes();
1925    }
1926
1927    class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
1928        @Override
1929        public boolean onPreDraw() {
1930            onChildViewsChanged(EVENT_PRE_DRAW);
1931            return true;
1932        }
1933    }
1934
1935    /**
1936     * Sorts child views with higher Z values to the beginning of a collection.
1937     */
1938    static class ViewElevationComparator implements Comparator<View> {
1939        @Override
1940        public int compare(View lhs, View rhs) {
1941            final float lz = ViewCompat.getZ(lhs);
1942            final float rz = ViewCompat.getZ(rhs);
1943            if (lz > rz) {
1944                return -1;
1945            } else if (lz < rz) {
1946                return 1;
1947            }
1948            return 0;
1949        }
1950    }
1951
1952    /**
1953     * Defines the default {@link Behavior} of a {@link View} class.
1954     *
1955     * <p>When writing a custom view, use this annotation to define the default behavior
1956     * when used as a direct child of an {@link CoordinatorLayout}. The default behavior
1957     * can be overridden using {@link LayoutParams#setBehavior}.</p>
1958     *
1959     * <p>Example: <code>@DefaultBehavior(MyBehavior.class)</code></p>
1960     */
1961    @Retention(RetentionPolicy.RUNTIME)
1962    public @interface DefaultBehavior {
1963        Class<? extends Behavior> value();
1964    }
1965
1966    /**
1967     * Interaction behavior plugin for child views of {@link CoordinatorLayout}.
1968     *
1969     * <p>A Behavior implements one or more interactions that a user can take on a child view.
1970     * These interactions may include drags, swipes, flings, or any other gestures.</p>
1971     *
1972     * @param <V> The View type that this Behavior operates on
1973     */
1974    public static abstract class Behavior<V extends View> {
1975
1976        /**
1977         * Default constructor for instantiating Behaviors.
1978         */
1979        public Behavior() {
1980        }
1981
1982        /**
1983         * Default constructor for inflating Behaviors from layout. The Behavior will have
1984         * the opportunity to parse specially defined layout parameters. These parameters will
1985         * appear on the child view tag.
1986         *
1987         * @param context
1988         * @param attrs
1989         */
1990        public Behavior(Context context, AttributeSet attrs) {
1991        }
1992
1993        /**
1994         * Called when the Behavior has been attached to a LayoutParams instance.
1995         *
1996         * <p>This will be called after the LayoutParams has been instantiated and can be
1997         * modified.</p>
1998         *
1999         * @param params the LayoutParams instance that this Behavior has been attached to
2000         */
2001        public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
2002        }
2003
2004        /**
2005         * Called when the Behavior has been detached from its holding LayoutParams instance.
2006         *
2007         * <p>This will only be called if the Behavior has been explicitly removed from the
2008         * LayoutParams instance via {@link LayoutParams#setBehavior(Behavior)}. It will not be
2009         * called if the associated view is removed from the CoordinatorLayout or similar.</p>
2010         */
2011        public void onDetachedFromLayoutParams() {
2012        }
2013
2014        /**
2015         * Respond to CoordinatorLayout touch events before they are dispatched to child views.
2016         *
2017         * <p>Behaviors can use this to monitor inbound touch events until one decides to
2018         * intercept the rest of the event stream to take an action on its associated child view.
2019         * This method will return false until it detects the proper intercept conditions, then
2020         * return true once those conditions have occurred.</p>
2021         *
2022         * <p>Once a Behavior intercepts touch events, the rest of the event stream will
2023         * be sent to the {@link #onTouchEvent} method.</p>
2024         *
2025         * <p>This method will be called regardless of the visibility of the associated child
2026         * of the behavior. If you only wish to handle touch events when the child is visible, you
2027         * should add a check to {@link View#isShown()} on the given child.</p>
2028         *
2029         * <p>The default implementation of this method always returns false.</p>
2030         *
2031         * @param parent the parent view currently receiving this touch event
2032         * @param child the child view associated with this Behavior
2033         * @param ev the MotionEvent describing the touch event being processed
2034         * @return true if this Behavior would like to intercept and take over the event stream.
2035         *         The default always returns false.
2036         */
2037        public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
2038            return false;
2039        }
2040
2041        /**
2042         * Respond to CoordinatorLayout touch events after this Behavior has started
2043         * {@link #onInterceptTouchEvent intercepting} them.
2044         *
2045         * <p>Behaviors may intercept touch events in order to help the CoordinatorLayout
2046         * manipulate its child views. For example, a Behavior may allow a user to drag a
2047         * UI pane open or closed. This method should perform actual mutations of view
2048         * layout state.</p>
2049         *
2050         * <p>This method will be called regardless of the visibility of the associated child
2051         * of the behavior. If you only wish to handle touch events when the child is visible, you
2052         * should add a check to {@link View#isShown()} on the given child.</p>
2053         *
2054         * @param parent the parent view currently receiving this touch event
2055         * @param child the child view associated with this Behavior
2056         * @param ev the MotionEvent describing the touch event being processed
2057         * @return true if this Behavior handled this touch event and would like to continue
2058         *         receiving events in this stream. The default always returns false.
2059         */
2060        public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
2061            return false;
2062        }
2063
2064        /**
2065         * Supply a scrim color that will be painted behind the associated child view.
2066         *
2067         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
2068         * interactive or actionable, drawing user focus and attention to the views above the scrim.
2069         * </p>
2070         *
2071         * <p>The default implementation returns {@link Color#BLACK}.</p>
2072         *
2073         * @param parent the parent view of the given child
2074         * @param child the child view above the scrim
2075         * @return the desired scrim color in 0xAARRGGBB format. The default return value is
2076         *         {@link Color#BLACK}.
2077         * @see #getScrimOpacity(CoordinatorLayout, android.view.View)
2078         */
2079        @ColorInt
2080        public int getScrimColor(CoordinatorLayout parent, V child) {
2081            return Color.BLACK;
2082        }
2083
2084        /**
2085         * Determine the current opacity of the scrim behind a given child view
2086         *
2087         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
2088         * interactive or actionable, drawing user focus and attention to the views above the scrim.
2089         * </p>
2090         *
2091         * <p>The default implementation returns 0.0f.</p>
2092         *
2093         * @param parent the parent view of the given child
2094         * @param child the child view above the scrim
2095         * @return the desired scrim opacity from 0.0f to 1.0f. The default return value is 0.0f.
2096         */
2097        @FloatRange(from = 0, to = 1)
2098        public float getScrimOpacity(CoordinatorLayout parent, V child) {
2099            return 0.f;
2100        }
2101
2102        /**
2103         * Determine whether interaction with views behind the given child in the child order
2104         * should be blocked.
2105         *
2106         * <p>The default implementation returns true if
2107         * {@link #getScrimOpacity(CoordinatorLayout, android.view.View)} would return > 0.0f.</p>
2108         *
2109         * @param parent the parent view of the given child
2110         * @param child the child view to test
2111         * @return true if {@link #getScrimOpacity(CoordinatorLayout, android.view.View)} would
2112         *         return > 0.0f.
2113         */
2114        public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
2115            return getScrimOpacity(parent, child) > 0.f;
2116        }
2117
2118        /**
2119         * Determine whether the supplied child view has another specific sibling view as a
2120         * layout dependency.
2121         *
2122         * <p>This method will be called at least once in response to a layout request. If it
2123         * returns true for a given child and dependency view pair, the parent CoordinatorLayout
2124         * will:</p>
2125         * <ol>
2126         *     <li>Always lay out this child after the dependent child is laid out, regardless
2127         *     of child order.</li>
2128         *     <li>Call {@link #onDependentViewChanged} when the dependency view's layout or
2129         *     position changes.</li>
2130         * </ol>
2131         *
2132         * @param parent the parent view of the given child
2133         * @param child the child view to test
2134         * @param dependency the proposed dependency of child
2135         * @return true if child's layout depends on the proposed dependency's layout,
2136         *         false otherwise
2137         *
2138         * @see #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)
2139         */
2140        public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
2141            return false;
2142        }
2143
2144        /**
2145         * Respond to a change in a child's dependent view
2146         *
2147         * <p>This method is called whenever a dependent view changes in size or position outside
2148         * of the standard layout flow. A Behavior may use this method to appropriately update
2149         * the child view in response.</p>
2150         *
2151         * <p>A view's dependency is determined by
2152         * {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)} or
2153         * if {@code child} has set another view as it's anchor.</p>
2154         *
2155         * <p>Note that if a Behavior changes the layout of a child via this method, it should
2156         * also be able to reconstruct the correct position in
2157         * {@link #onLayoutChild(CoordinatorLayout, android.view.View, int) onLayoutChild}.
2158         * <code>onDependentViewChanged</code> will not be called during normal layout since
2159         * the layout of each child view will always happen in dependency order.</p>
2160         *
2161         * <p>If the Behavior changes the child view's size or position, it should return true.
2162         * The default implementation returns false.</p>
2163         *
2164         * @param parent the parent view of the given child
2165         * @param child the child view to manipulate
2166         * @param dependency the dependent view that changed
2167         * @return true if the Behavior changed the child view's size or position, false otherwise
2168         */
2169        public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
2170            return false;
2171        }
2172
2173        /**
2174         * Respond to a child's dependent view being removed.
2175         *
2176         * <p>This method is called after a dependent view has been removed from the parent.
2177         * A Behavior may use this method to appropriately update the child view in response.</p>
2178         *
2179         * <p>A view's dependency is determined by
2180         * {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)} or
2181         * if {@code child} has set another view as it's anchor.</p>
2182         *
2183         * @param parent the parent view of the given child
2184         * @param child the child view to manipulate
2185         * @param dependency the dependent view that has been removed
2186         */
2187        public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {
2188        }
2189
2190        /**
2191         * Called when the parent CoordinatorLayout is about to measure the given child view.
2192         *
2193         * <p>This method can be used to perform custom or modified measurement of a child view
2194         * in place of the default child measurement behavior. The Behavior's implementation
2195         * can delegate to the standard CoordinatorLayout measurement behavior by calling
2196         * {@link CoordinatorLayout#onMeasureChild(android.view.View, int, int, int, int)
2197         * parent.onMeasureChild}.</p>
2198         *
2199         * @param parent the parent CoordinatorLayout
2200         * @param child the child to measure
2201         * @param parentWidthMeasureSpec the width requirements for this view
2202         * @param widthUsed extra space that has been used up by the parent
2203         *        horizontally (possibly by other children of the parent)
2204         * @param parentHeightMeasureSpec the height requirements for this view
2205         * @param heightUsed extra space that has been used up by the parent
2206         *        vertically (possibly by other children of the parent)
2207         * @return true if the Behavior measured the child view, false if the CoordinatorLayout
2208         *         should perform its default measurement
2209         */
2210        public boolean onMeasureChild(CoordinatorLayout parent, V child,
2211                int parentWidthMeasureSpec, int widthUsed,
2212                int parentHeightMeasureSpec, int heightUsed) {
2213            return false;
2214        }
2215
2216        /**
2217         * Called when the parent CoordinatorLayout is about the lay out the given child view.
2218         *
2219         * <p>This method can be used to perform custom or modified layout of a child view
2220         * in place of the default child layout behavior. The Behavior's implementation can
2221         * delegate to the standard CoordinatorLayout measurement behavior by calling
2222         * {@link CoordinatorLayout#onLayoutChild(android.view.View, int)
2223         * parent.onLayoutChild}.</p>
2224         *
2225         * <p>If a Behavior implements
2226         * {@link #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)}
2227         * to change the position of a view in response to a dependent view changing, it
2228         * should also implement <code>onLayoutChild</code> in such a way that respects those
2229         * dependent views. <code>onLayoutChild</code> will always be called for a dependent view
2230         * <em>after</em> its dependency has been laid out.</p>
2231         *
2232         * @param parent the parent CoordinatorLayout
2233         * @param child child view to lay out
2234         * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
2235         *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
2236         *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
2237         * @return true if the Behavior performed layout of the child view, false to request
2238         *         default layout behavior
2239         */
2240        public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
2241            return false;
2242        }
2243
2244        // Utility methods for accessing child-specific, behavior-modifiable properties.
2245
2246        /**
2247         * Associate a Behavior-specific tag object with the given child view.
2248         * This object will be stored with the child view's LayoutParams.
2249         *
2250         * @param child child view to set tag with
2251         * @param tag tag object to set
2252         */
2253        public static void setTag(View child, Object tag) {
2254            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
2255            lp.mBehaviorTag = tag;
2256        }
2257
2258        /**
2259         * Get the behavior-specific tag object with the given child view.
2260         * This object is stored with the child view's LayoutParams.
2261         *
2262         * @param child child view to get tag with
2263         * @return the previously stored tag object
2264         */
2265        public static Object getTag(View child) {
2266            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
2267            return lp.mBehaviorTag;
2268        }
2269
2270        /**
2271         * @deprecated You should now override
2272         * {@link #onStartNestedScroll(CoordinatorLayout, View, View, View, int, int)}. This
2273         * method will still continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
2274         */
2275        @Deprecated
2276        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
2277                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
2278                @ScrollAxis int axes) {
2279            return false;
2280        }
2281
2282        /**
2283         * Called when a descendant of the CoordinatorLayout attempts to initiate a nested scroll.
2284         *
2285         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may respond
2286         * to this event and return true to indicate that the CoordinatorLayout should act as
2287         * a nested scrolling parent for this scroll. Only Behaviors that return true from
2288         * this method will receive subsequent nested scroll events.</p>
2289         *
2290         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2291         *                          associated with
2292         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2293         * @param directTargetChild the child view of the CoordinatorLayout that either is or
2294         *                          contains the target of the nested scroll operation
2295         * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
2296         * @param axes the axes that this nested scroll applies to. See
2297         *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
2298         *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
2299         * @param type the type of input which cause this scroll event
2300         * @return true if the Behavior wishes to accept this nested scroll
2301         *
2302         * @see NestedScrollingParent2#onStartNestedScroll(View, View, int, int)
2303         */
2304        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
2305                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
2306                @ScrollAxis int axes, @NestedScrollType int type) {
2307            if (type == ViewCompat.TYPE_TOUCH) {
2308                return onStartNestedScroll(coordinatorLayout, child, directTargetChild,
2309                        target, axes);
2310            }
2311            return false;
2312        }
2313
2314        /**
2315         * @deprecated You should now override
2316         * {@link #onNestedScrollAccepted(CoordinatorLayout, View, View, View, int, int)}. This
2317         * method will still continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
2318         */
2319        @Deprecated
2320        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout,
2321                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
2322                @ScrollAxis int axes) {
2323            // Do nothing
2324        }
2325
2326        /**
2327         * Called when a nested scroll has been accepted by the CoordinatorLayout.
2328         *
2329         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
2330         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2331         * that returned true will receive subsequent nested scroll events for that nested scroll.
2332         * </p>
2333         *
2334         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2335         *                          associated with
2336         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2337         * @param directTargetChild the child view of the CoordinatorLayout that either is or
2338         *                          contains the target of the nested scroll operation
2339         * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
2340         * @param axes the axes that this nested scroll applies to. See
2341         *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
2342         *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
2343         * @param type the type of input which cause this scroll event
2344         *
2345         * @see NestedScrollingParent2#onNestedScrollAccepted(View, View, int, int)
2346         */
2347        public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout,
2348                @NonNull V child, @NonNull View directTargetChild, @NonNull View target,
2349                @ScrollAxis int axes, @NestedScrollType int type) {
2350            if (type == ViewCompat.TYPE_TOUCH) {
2351                onNestedScrollAccepted(coordinatorLayout, child, directTargetChild,
2352                        target, axes);
2353            }
2354        }
2355
2356        /**
2357         * @deprecated You should now override
2358         * {@link #onStopNestedScroll(CoordinatorLayout, View, View, int)}. This method will still
2359         * continue to be called if the type is {@link ViewCompat#TYPE_TOUCH}.
2360         */
2361        @Deprecated
2362        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
2363                @NonNull V child, @NonNull View target) {
2364            // Do nothing
2365        }
2366
2367        /**
2368         * Called when a nested scroll has ended.
2369         *
2370         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
2371         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2372         * that returned true will receive subsequent nested scroll events for that nested scroll.
2373         * </p>
2374         *
2375         * <p><code>onStopNestedScroll</code> marks the end of a single nested scroll event
2376         * sequence. This is a good place to clean up any state related to the nested scroll.
2377         * </p>
2378         *
2379         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2380         *                          associated with
2381         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2382         * @param target the descendant view of the CoordinatorLayout that initiated
2383         *               the nested scroll
2384         * @param type the type of input which cause this scroll event
2385         *
2386         * @see NestedScrollingParent2#onStopNestedScroll(View, int)
2387         */
2388        public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
2389                @NonNull V child, @NonNull View target, @NestedScrollType int type) {
2390            if (type == ViewCompat.TYPE_TOUCH) {
2391                onStopNestedScroll(coordinatorLayout, child, target);
2392            }
2393        }
2394
2395        /**
2396         * @deprecated You should now override
2397         * {@link #onNestedScroll(CoordinatorLayout, View, View, int, int, int, int, int)}.
2398         * This method will still continue to be called if the type is
2399         * {@link ViewCompat#TYPE_TOUCH}.
2400         */
2401        @Deprecated
2402        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child,
2403                @NonNull View target, int dxConsumed, int dyConsumed,
2404                int dxUnconsumed, int dyUnconsumed) {
2405            // Do nothing
2406        }
2407
2408        /**
2409         * Called when a nested scroll in progress has updated and the target has scrolled or
2410         * attempted to scroll.
2411         *
2412         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
2413         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2414         * that returned true will receive subsequent nested scroll events for that nested scroll.
2415         * </p>
2416         *
2417         * <p><code>onNestedScroll</code> is called each time the nested scroll is updated by the
2418         * nested scrolling child, with both consumed and unconsumed components of the scroll
2419         * supplied in pixels. <em>Each Behavior responding to the nested scroll will receive the
2420         * same values.</em>
2421         * </p>
2422         *
2423         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2424         *                          associated with
2425         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2426         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
2427         * @param dxConsumed horizontal pixels consumed by the target's own scrolling operation
2428         * @param dyConsumed vertical pixels consumed by the target's own scrolling operation
2429         * @param dxUnconsumed horizontal pixels not consumed by the target's own scrolling
2430         *                     operation, but requested by the user
2431         * @param dyUnconsumed vertical pixels not consumed by the target's own scrolling operation,
2432         *                     but requested by the user
2433         * @param type the type of input which cause this scroll event
2434         *
2435         * @see NestedScrollingParent2#onNestedScroll(View, int, int, int, int, int)
2436         */
2437        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child,
2438                @NonNull View target, int dxConsumed, int dyConsumed,
2439                int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type) {
2440            if (type == ViewCompat.TYPE_TOUCH) {
2441                onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
2442                        dxUnconsumed, dyUnconsumed);
2443            }
2444        }
2445
2446        /**
2447         * @deprecated You should now override
2448         * {@link #onNestedPreScroll(CoordinatorLayout, View, View, int, int, int[], int)}.
2449         * This method will still continue to be called if the type is
2450         * {@link ViewCompat#TYPE_TOUCH}.
2451         */
2452        @Deprecated
2453        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
2454                @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed) {
2455            // Do nothing
2456        }
2457
2458        /**
2459         * Called when a nested scroll in progress is about to update, before the target has
2460         * consumed any of the scrolled distance.
2461         *
2462         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
2463         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2464         * that returned true will receive subsequent nested scroll events for that nested scroll.
2465         * </p>
2466         *
2467         * <p><code>onNestedPreScroll</code> is called each time the nested scroll is updated
2468         * by the nested scrolling child, before the nested scrolling child has consumed the scroll
2469         * distance itself. <em>Each Behavior responding to the nested scroll will receive the
2470         * same values.</em> The CoordinatorLayout will report as consumed the maximum number
2471         * of pixels in either direction that any Behavior responding to the nested scroll reported
2472         * as consumed.</p>
2473         *
2474         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2475         *                          associated with
2476         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2477         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
2478         * @param dx the raw horizontal number of pixels that the user attempted to scroll
2479         * @param dy the raw vertical number of pixels that the user attempted to scroll
2480         * @param consumed out parameter. consumed[0] should be set to the distance of dx that
2481         *                 was consumed, consumed[1] should be set to the distance of dy that
2482         *                 was consumed
2483         * @param type the type of input which cause this scroll event
2484         *
2485         * @see NestedScrollingParent2#onNestedPreScroll(View, int, int, int[], int)
2486         */
2487        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
2488                @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed,
2489                @NestedScrollType int type) {
2490            if (type == ViewCompat.TYPE_TOUCH) {
2491                onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
2492            }
2493        }
2494
2495        /**
2496         * Called when a nested scrolling child is starting a fling or an action that would
2497         * be a fling.
2498         *
2499         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
2500         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2501         * that returned true will receive subsequent nested scroll events for that nested scroll.
2502         * </p>
2503         *
2504         * <p><code>onNestedFling</code> is called when the current nested scrolling child view
2505         * detects the proper conditions for a fling. It reports if the child itself consumed
2506         * the fling. If it did not, the child is expected to show some sort of overscroll
2507         * indication. This method should return true if it consumes the fling, so that a child
2508         * that did not itself take an action in response can choose not to show an overfling
2509         * indication.</p>
2510         *
2511         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2512         *                          associated with
2513         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2514         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
2515         * @param velocityX horizontal velocity of the attempted fling
2516         * @param velocityY vertical velocity of the attempted fling
2517         * @param consumed true if the nested child view consumed the fling
2518         * @return true if the Behavior consumed the fling
2519         *
2520         * @see NestedScrollingParent#onNestedFling(View, float, float, boolean)
2521         */
2522        public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout,
2523                @NonNull V child, @NonNull View target, float velocityX, float velocityY,
2524                boolean consumed) {
2525            return false;
2526        }
2527
2528        /**
2529         * Called when a nested scrolling child is about to start a fling.
2530         *
2531         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
2532         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
2533         * that returned true will receive subsequent nested scroll events for that nested scroll.
2534         * </p>
2535         *
2536         * <p><code>onNestedPreFling</code> is called when the current nested scrolling child view
2537         * detects the proper conditions for a fling, but it has not acted on it yet. A
2538         * Behavior can return true to indicate that it consumed the fling. If at least one
2539         * Behavior returns true, the fling should not be acted upon by the child.</p>
2540         *
2541         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2542         *                          associated with
2543         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2544         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
2545         * @param velocityX horizontal velocity of the attempted fling
2546         * @param velocityY vertical velocity of the attempted fling
2547         * @return true if the Behavior consumed the fling
2548         *
2549         * @see NestedScrollingParent#onNestedPreFling(View, float, float)
2550         */
2551        public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,
2552                @NonNull V child, @NonNull View target, float velocityX, float velocityY) {
2553            return false;
2554        }
2555
2556        /**
2557         * Called when the window insets have changed.
2558         *
2559         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
2560         * to handle the window inset change on behalf of it's associated view.
2561         * </p>
2562         *
2563         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2564         *                          associated with
2565         * @param child the child view of the CoordinatorLayout this Behavior is associated with
2566         * @param insets the new window insets.
2567         *
2568         * @return The insets supplied, minus any insets that were consumed
2569         */
2570        @NonNull
2571        public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout,
2572                V child, WindowInsetsCompat insets) {
2573            return insets;
2574        }
2575
2576        /**
2577         * Called when a child of the view associated with this behavior wants a particular
2578         * rectangle to be positioned onto the screen.
2579         *
2580         * <p>The contract for this method is the same as
2581         * {@link ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)}.</p>
2582         *
2583         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
2584         *                          associated with
2585         * @param child             the child view of the CoordinatorLayout this Behavior is
2586         *                          associated with
2587         * @param rectangle         The rectangle which the child wishes to be on the screen
2588         *                          in the child's coordinates
2589         * @param immediate         true to forbid animated or delayed scrolling, false otherwise
2590         * @return true if the Behavior handled the request
2591         * @see ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)
2592         */
2593        public boolean onRequestChildRectangleOnScreen(CoordinatorLayout coordinatorLayout,
2594                V child, Rect rectangle, boolean immediate) {
2595            return false;
2596        }
2597
2598        /**
2599         * Hook allowing a behavior to re-apply a representation of its internal state that had
2600         * previously been generated by {@link #onSaveInstanceState}. This function will never
2601         * be called with a null state.
2602         *
2603         * @param parent the parent CoordinatorLayout
2604         * @param child child view to restore from
2605         * @param state The frozen state that had previously been returned by
2606         *        {@link #onSaveInstanceState}.
2607         *
2608         * @see #onSaveInstanceState()
2609         */
2610        public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
2611            // no-op
2612        }
2613
2614        /**
2615         * Hook allowing a behavior to generate a representation of its internal state
2616         * that can later be used to create a new instance with that same state.
2617         * This state should only contain information that is not persistent or can
2618         * not be reconstructed later.
2619         *
2620         * <p>Behavior state is only saved when both the parent {@link CoordinatorLayout} and
2621         * a view using this behavior have valid IDs set.</p>
2622         *
2623         * @param parent the parent CoordinatorLayout
2624         * @param child child view to restore from
2625         *
2626         * @return Returns a Parcelable object containing the behavior's current dynamic
2627         *         state.
2628         *
2629         * @see #onRestoreInstanceState(android.os.Parcelable)
2630         * @see View#onSaveInstanceState()
2631         */
2632        public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
2633            return BaseSavedState.EMPTY_STATE;
2634        }
2635
2636        /**
2637         * Called when a view is set to dodge view insets.
2638         *
2639         * <p>This method allows a behavior to update the rectangle that should be dodged.
2640         * The rectangle should be in the parent's coordinate system and within the child's
2641         * bounds. If not, a {@link IllegalArgumentException} is thrown.</p>
2642         *
2643         * @param parent the CoordinatorLayout parent of the view this Behavior is
2644         *               associated with
2645         * @param child  the child view of the CoordinatorLayout this Behavior is associated with
2646         * @param rect   the rect to update with the dodge rectangle
2647         * @return true the rect was updated, false if we should use the child's bounds
2648         */
2649        public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child,
2650                @NonNull Rect rect) {
2651            return false;
2652        }
2653    }
2654
2655    /**
2656     * Parameters describing the desired layout for a child of a {@link CoordinatorLayout}.
2657     */
2658    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
2659        /**
2660         * A {@link Behavior} that the child view should obey.
2661         */
2662        Behavior mBehavior;
2663
2664        boolean mBehaviorResolved = false;
2665
2666        /**
2667         * A {@link Gravity} value describing how this child view should lay out.
2668         * If either or both of the axes are not specified, they are treated by CoordinatorLayout
2669         * as {@link Gravity#TOP} or {@link GravityCompat#START}. If an
2670         * {@link #setAnchorId(int) anchor} is also specified, the gravity describes how this child
2671         * view should be positioned relative to its anchored position.
2672         */
2673        public int gravity = Gravity.NO_GRAVITY;
2674
2675        /**
2676         * A {@link Gravity} value describing which edge of a child view's
2677         * {@link #getAnchorId() anchor} view the child should position itself relative to.
2678         */
2679        public int anchorGravity = Gravity.NO_GRAVITY;
2680
2681        /**
2682         * The index of the horizontal keyline specified to the parent CoordinatorLayout that this
2683         * child should align relative to. If an {@link #setAnchorId(int) anchor} is present the
2684         * keyline will be ignored.
2685         */
2686        public int keyline = -1;
2687
2688        /**
2689         * A {@link View#getId() view id} of a descendant view of the CoordinatorLayout that
2690         * this child should position relative to.
2691         */
2692        int mAnchorId = View.NO_ID;
2693
2694        /**
2695         * A {@link Gravity} value describing how this child view insets the CoordinatorLayout.
2696         * Other child views which are set to dodge the same inset edges will be moved appropriately
2697         * so that the views do not overlap.
2698         */
2699        public int insetEdge = Gravity.NO_GRAVITY;
2700
2701        /**
2702         * A {@link Gravity} value describing how this child view dodges any inset child views in
2703         * the CoordinatorLayout. Any views which are inset on the same edge as this view is set to
2704         * dodge will result in this view being moved so that the views do not overlap.
2705         */
2706        public int dodgeInsetEdges = Gravity.NO_GRAVITY;
2707
2708        int mInsetOffsetX;
2709        int mInsetOffsetY;
2710
2711        View mAnchorView;
2712        View mAnchorDirectChild;
2713
2714        private boolean mDidBlockInteraction;
2715        private boolean mDidAcceptNestedScrollTouch;
2716        private boolean mDidAcceptNestedScrollNonTouch;
2717        private boolean mDidChangeAfterNestedScroll;
2718
2719        final Rect mLastChildRect = new Rect();
2720
2721        Object mBehaviorTag;
2722
2723        public LayoutParams(int width, int height) {
2724            super(width, height);
2725        }
2726
2727        LayoutParams(Context context, AttributeSet attrs) {
2728            super(context, attrs);
2729
2730            final TypedArray a = context.obtainStyledAttributes(attrs,
2731                    R.styleable.CoordinatorLayout_Layout);
2732
2733            this.gravity = a.getInteger(
2734                    R.styleable.CoordinatorLayout_Layout_android_layout_gravity,
2735                    Gravity.NO_GRAVITY);
2736            mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_Layout_layout_anchor,
2737                    View.NO_ID);
2738            this.anchorGravity = a.getInteger(
2739                    R.styleable.CoordinatorLayout_Layout_layout_anchorGravity,
2740                    Gravity.NO_GRAVITY);
2741
2742            this.keyline = a.getInteger(R.styleable.CoordinatorLayout_Layout_layout_keyline,
2743                    -1);
2744
2745            insetEdge = a.getInt(R.styleable.CoordinatorLayout_Layout_layout_insetEdge, 0);
2746            dodgeInsetEdges = a.getInt(
2747                    R.styleable.CoordinatorLayout_Layout_layout_dodgeInsetEdges, 0);
2748            mBehaviorResolved = a.hasValue(
2749                    R.styleable.CoordinatorLayout_Layout_layout_behavior);
2750            if (mBehaviorResolved) {
2751                mBehavior = parseBehavior(context, attrs, a.getString(
2752                        R.styleable.CoordinatorLayout_Layout_layout_behavior));
2753            }
2754            a.recycle();
2755
2756            if (mBehavior != null) {
2757                // If we have a Behavior, dispatch that it has been attached
2758                mBehavior.onAttachedToLayoutParams(this);
2759            }
2760        }
2761
2762        public LayoutParams(LayoutParams p) {
2763            super(p);
2764        }
2765
2766        public LayoutParams(MarginLayoutParams p) {
2767            super(p);
2768        }
2769
2770        public LayoutParams(ViewGroup.LayoutParams p) {
2771            super(p);
2772        }
2773
2774        /**
2775         * Get the id of this view's anchor.
2776         *
2777         * @return A {@link View#getId() view id} or {@link View#NO_ID} if there is no anchor
2778         */
2779        @IdRes
2780        public int getAnchorId() {
2781            return mAnchorId;
2782        }
2783
2784        /**
2785         * Set the id of this view's anchor.
2786         *
2787         * <p>The view with this id must be a descendant of the CoordinatorLayout containing
2788         * the child view this LayoutParams belongs to. It may not be the child view with
2789         * this LayoutParams or a descendant of it.</p>
2790         *
2791         * @param id The {@link View#getId() view id} of the anchor or
2792         *           {@link View#NO_ID} if there is no anchor
2793         */
2794        public void setAnchorId(@IdRes int id) {
2795            invalidateAnchor();
2796            mAnchorId = id;
2797        }
2798
2799        /**
2800         * Get the behavior governing the layout and interaction of the child view within
2801         * a parent CoordinatorLayout.
2802         *
2803         * @return The current behavior or null if no behavior is specified
2804         */
2805        @Nullable
2806        public Behavior getBehavior() {
2807            return mBehavior;
2808        }
2809
2810        /**
2811         * Set the behavior governing the layout and interaction of the child view within
2812         * a parent CoordinatorLayout.
2813         *
2814         * <p>Setting a new behavior will remove any currently associated
2815         * {@link Behavior#setTag(android.view.View, Object) Behavior tag}.</p>
2816         *
2817         * @param behavior The behavior to set or null for no special behavior
2818         */
2819        public void setBehavior(@Nullable Behavior behavior) {
2820            if (mBehavior != behavior) {
2821                if (mBehavior != null) {
2822                    // First detach any old behavior
2823                    mBehavior.onDetachedFromLayoutParams();
2824                }
2825
2826                mBehavior = behavior;
2827                mBehaviorTag = null;
2828                mBehaviorResolved = true;
2829
2830                if (behavior != null) {
2831                    // Now dispatch that the Behavior has been attached
2832                    behavior.onAttachedToLayoutParams(this);
2833                }
2834            }
2835        }
2836
2837        /**
2838         * Set the last known position rect for this child view
2839         * @param r the rect to set
2840         */
2841        void setLastChildRect(Rect r) {
2842            mLastChildRect.set(r);
2843        }
2844
2845        /**
2846         * Get the last known position rect for this child view.
2847         * Note: do not mutate the result of this call.
2848         */
2849        Rect getLastChildRect() {
2850            return mLastChildRect;
2851        }
2852
2853        /**
2854         * Returns true if the anchor id changed to another valid view id since the anchor view
2855         * was resolved.
2856         */
2857        boolean checkAnchorChanged() {
2858            return mAnchorView == null && mAnchorId != View.NO_ID;
2859        }
2860
2861        /**
2862         * Returns true if the associated Behavior previously blocked interaction with other views
2863         * below the associated child since the touch behavior tracking was last
2864         * {@link #resetTouchBehaviorTracking() reset}.
2865         *
2866         * @see #isBlockingInteractionBelow(CoordinatorLayout, android.view.View)
2867         */
2868        boolean didBlockInteraction() {
2869            if (mBehavior == null) {
2870                mDidBlockInteraction = false;
2871            }
2872            return mDidBlockInteraction;
2873        }
2874
2875        /**
2876         * Check if the associated Behavior wants to block interaction below the given child
2877         * view. The given child view should be the child this LayoutParams is associated with.
2878         *
2879         * <p>Once interaction is blocked, it will remain blocked until touch interaction tracking
2880         * is {@link #resetTouchBehaviorTracking() reset}.</p>
2881         *
2882         * @param parent the parent CoordinatorLayout
2883         * @param child the child view this LayoutParams is associated with
2884         * @return true to block interaction below the given child
2885         */
2886        boolean isBlockingInteractionBelow(CoordinatorLayout parent, View child) {
2887            if (mDidBlockInteraction) {
2888                return true;
2889            }
2890
2891            return mDidBlockInteraction |= mBehavior != null
2892                    ? mBehavior.blocksInteractionBelow(parent, child)
2893                    : false;
2894        }
2895
2896        /**
2897         * Reset tracking of Behavior-specific touch interactions. This includes
2898         * interaction blocking.
2899         *
2900         * @see #isBlockingInteractionBelow(CoordinatorLayout, android.view.View)
2901         * @see #didBlockInteraction()
2902         */
2903        void resetTouchBehaviorTracking() {
2904            mDidBlockInteraction = false;
2905        }
2906
2907        void resetNestedScroll(int type) {
2908            setNestedScrollAccepted(type, false);
2909        }
2910
2911        void setNestedScrollAccepted(int type, boolean accept) {
2912            switch (type) {
2913                case ViewCompat.TYPE_TOUCH:
2914                    mDidAcceptNestedScrollTouch = accept;
2915                    break;
2916                case ViewCompat.TYPE_NON_TOUCH:
2917                    mDidAcceptNestedScrollNonTouch = accept;
2918                    break;
2919            }
2920        }
2921
2922        boolean isNestedScrollAccepted(int type) {
2923            switch (type) {
2924                case ViewCompat.TYPE_TOUCH:
2925                    return mDidAcceptNestedScrollTouch;
2926                case ViewCompat.TYPE_NON_TOUCH:
2927                    return mDidAcceptNestedScrollNonTouch;
2928            }
2929            return false;
2930        }
2931
2932        boolean getChangedAfterNestedScroll() {
2933            return mDidChangeAfterNestedScroll;
2934        }
2935
2936        void setChangedAfterNestedScroll(boolean changed) {
2937            mDidChangeAfterNestedScroll = changed;
2938        }
2939
2940        void resetChangedAfterNestedScroll() {
2941            mDidChangeAfterNestedScroll = false;
2942        }
2943
2944        /**
2945         * Check if an associated child view depends on another child view of the CoordinatorLayout.
2946         *
2947         * @param parent the parent CoordinatorLayout
2948         * @param child the child to check
2949         * @param dependency the proposed dependency to check
2950         * @return true if child depends on dependency
2951         */
2952        boolean dependsOn(CoordinatorLayout parent, View child, View dependency) {
2953            return dependency == mAnchorDirectChild
2954                    || shouldDodge(dependency, ViewCompat.getLayoutDirection(parent))
2955                    || (mBehavior != null && mBehavior.layoutDependsOn(parent, child, dependency));
2956        }
2957
2958        /**
2959         * Invalidate the cached anchor view and direct child ancestor of that anchor.
2960         * The anchor will need to be
2961         * {@link #findAnchorView(CoordinatorLayout, android.view.View) found} before
2962         * being used again.
2963         */
2964        void invalidateAnchor() {
2965            mAnchorView = mAnchorDirectChild = null;
2966        }
2967
2968        /**
2969         * Locate the appropriate anchor view by the current {@link #setAnchorId(int) anchor id}
2970         * or return the cached anchor view if already known.
2971         *
2972         * @param parent the parent CoordinatorLayout
2973         * @param forChild the child this LayoutParams is associated with
2974         * @return the located descendant anchor view, or null if the anchor id is
2975         *         {@link View#NO_ID}.
2976         */
2977        View findAnchorView(CoordinatorLayout parent, View forChild) {
2978            if (mAnchorId == View.NO_ID) {
2979                mAnchorView = mAnchorDirectChild = null;
2980                return null;
2981            }
2982
2983            if (mAnchorView == null || !verifyAnchorView(forChild, parent)) {
2984                resolveAnchorView(forChild, parent);
2985            }
2986            return mAnchorView;
2987        }
2988
2989        /**
2990         * Determine the anchor view for the child view this LayoutParams is assigned to.
2991         * Assumes mAnchorId is valid.
2992         */
2993        private void resolveAnchorView(final View forChild, final CoordinatorLayout parent) {
2994            mAnchorView = parent.findViewById(mAnchorId);
2995            if (mAnchorView != null) {
2996                if (mAnchorView == parent) {
2997                    if (parent.isInEditMode()) {
2998                        mAnchorView = mAnchorDirectChild = null;
2999                        return;
3000                    }
3001                    throw new IllegalStateException(
3002                            "View can not be anchored to the the parent CoordinatorLayout");
3003                }
3004
3005                View directChild = mAnchorView;
3006                for (ViewParent p = mAnchorView.getParent();
3007                        p != parent && p != null;
3008                        p = p.getParent()) {
3009                    if (p == forChild) {
3010                        if (parent.isInEditMode()) {
3011                            mAnchorView = mAnchorDirectChild = null;
3012                            return;
3013                        }
3014                        throw new IllegalStateException(
3015                                "Anchor must not be a descendant of the anchored view");
3016                    }
3017                    if (p instanceof View) {
3018                        directChild = (View) p;
3019                    }
3020                }
3021                mAnchorDirectChild = directChild;
3022            } else {
3023                if (parent.isInEditMode()) {
3024                    mAnchorView = mAnchorDirectChild = null;
3025                    return;
3026                }
3027                throw new IllegalStateException("Could not find CoordinatorLayout descendant view"
3028                        + " with id " + parent.getResources().getResourceName(mAnchorId)
3029                        + " to anchor view " + forChild);
3030            }
3031        }
3032
3033        /**
3034         * Verify that the previously resolved anchor view is still valid - that it is still
3035         * a descendant of the expected parent view, it is not the child this LayoutParams
3036         * is assigned to or a descendant of it, and it has the expected id.
3037         */
3038        private boolean verifyAnchorView(View forChild, CoordinatorLayout parent) {
3039            if (mAnchorView.getId() != mAnchorId) {
3040                return false;
3041            }
3042
3043            View directChild = mAnchorView;
3044            for (ViewParent p = mAnchorView.getParent();
3045                    p != parent;
3046                    p = p.getParent()) {
3047                if (p == null || p == forChild) {
3048                    mAnchorView = mAnchorDirectChild = null;
3049                    return false;
3050                }
3051                if (p instanceof View) {
3052                    directChild = (View) p;
3053                }
3054            }
3055            mAnchorDirectChild = directChild;
3056            return true;
3057        }
3058
3059        /**
3060         * Checks whether the view with this LayoutParams should dodge the specified view.
3061         */
3062        private boolean shouldDodge(View other, int layoutDirection) {
3063            LayoutParams lp = (LayoutParams) other.getLayoutParams();
3064            final int absInset = GravityCompat.getAbsoluteGravity(lp.insetEdge, layoutDirection);
3065            return absInset != Gravity.NO_GRAVITY && (absInset &
3066                    GravityCompat.getAbsoluteGravity(dodgeInsetEdges, layoutDirection)) == absInset;
3067        }
3068    }
3069
3070    private class HierarchyChangeListener implements OnHierarchyChangeListener {
3071        HierarchyChangeListener() {
3072        }
3073
3074        @Override
3075        public void onChildViewAdded(View parent, View child) {
3076            if (mOnHierarchyChangeListener != null) {
3077                mOnHierarchyChangeListener.onChildViewAdded(parent, child);
3078            }
3079        }
3080
3081        @Override
3082        public void onChildViewRemoved(View parent, View child) {
3083            onChildViewsChanged(EVENT_VIEW_REMOVED);
3084
3085            if (mOnHierarchyChangeListener != null) {
3086                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
3087            }
3088        }
3089    }
3090
3091    @Override
3092    protected void onRestoreInstanceState(Parcelable state) {
3093        if (!(state instanceof SavedState)) {
3094            super.onRestoreInstanceState(state);
3095            return;
3096        }
3097
3098        final SavedState ss = (SavedState) state;
3099        super.onRestoreInstanceState(ss.getSuperState());
3100
3101        final SparseArray<Parcelable> behaviorStates = ss.behaviorStates;
3102
3103        for (int i = 0, count = getChildCount(); i < count; i++) {
3104            final View child = getChildAt(i);
3105            final int childId = child.getId();
3106            final LayoutParams lp = getResolvedLayoutParams(child);
3107            final Behavior b = lp.getBehavior();
3108
3109            if (childId != NO_ID && b != null) {
3110                Parcelable savedState = behaviorStates.get(childId);
3111                if (savedState != null) {
3112                    b.onRestoreInstanceState(this, child, savedState);
3113                }
3114            }
3115        }
3116    }
3117
3118    @Override
3119    protected Parcelable onSaveInstanceState() {
3120        final SavedState ss = new SavedState(super.onSaveInstanceState());
3121
3122        final SparseArray<Parcelable> behaviorStates = new SparseArray<>();
3123        for (int i = 0, count = getChildCount(); i < count; i++) {
3124            final View child = getChildAt(i);
3125            final int childId = child.getId();
3126            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
3127            final Behavior b = lp.getBehavior();
3128
3129            if (childId != NO_ID && b != null) {
3130                // If the child has an ID and a Behavior, let it save some state...
3131                Parcelable state = b.onSaveInstanceState(this, child);
3132                if (state != null) {
3133                    behaviorStates.append(childId, state);
3134                }
3135            }
3136        }
3137        ss.behaviorStates = behaviorStates;
3138        return ss;
3139    }
3140
3141    @Override
3142    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
3143        final CoordinatorLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
3144        final Behavior behavior = lp.getBehavior();
3145
3146        if (behavior != null
3147                && behavior.onRequestChildRectangleOnScreen(this, child, rectangle, immediate)) {
3148            return true;
3149        }
3150
3151        return super.requestChildRectangleOnScreen(child, rectangle, immediate);
3152    }
3153
3154    private void setupForInsets() {
3155        if (Build.VERSION.SDK_INT < 21) {
3156            return;
3157        }
3158
3159        if (ViewCompat.getFitsSystemWindows(this)) {
3160            if (mApplyWindowInsetsListener == null) {
3161                mApplyWindowInsetsListener =
3162                        new android.support.v4.view.OnApplyWindowInsetsListener() {
3163                            @Override
3164                            public WindowInsetsCompat onApplyWindowInsets(View v,
3165                                    WindowInsetsCompat insets) {
3166                                return setWindowInsets(insets);
3167                            }
3168                        };
3169            }
3170            // First apply the insets listener
3171            ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
3172
3173            // Now set the sys ui flags to enable us to lay out in the window insets
3174            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
3175                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
3176        } else {
3177            ViewCompat.setOnApplyWindowInsetsListener(this, null);
3178        }
3179    }
3180
3181    protected static class SavedState extends AbsSavedState {
3182        SparseArray<Parcelable> behaviorStates;
3183
3184        public SavedState(Parcel source, ClassLoader loader) {
3185            super(source, loader);
3186
3187            final int size = source.readInt();
3188
3189            final int[] ids = new int[size];
3190            source.readIntArray(ids);
3191
3192            final Parcelable[] states = source.readParcelableArray(loader);
3193
3194            behaviorStates = new SparseArray<>(size);
3195            for (int i = 0; i < size; i++) {
3196                behaviorStates.append(ids[i], states[i]);
3197            }
3198        }
3199
3200        public SavedState(Parcelable superState) {
3201            super(superState);
3202        }
3203
3204        @Override
3205        public void writeToParcel(Parcel dest, int flags) {
3206            super.writeToParcel(dest, flags);
3207
3208            final int size = behaviorStates != null ? behaviorStates.size() : 0;
3209            dest.writeInt(size);
3210
3211            final int[] ids = new int[size];
3212            final Parcelable[] states = new Parcelable[size];
3213
3214            for (int i = 0; i < size; i++) {
3215                ids[i] = behaviorStates.keyAt(i);
3216                states[i] = behaviorStates.valueAt(i);
3217            }
3218            dest.writeIntArray(ids);
3219            dest.writeParcelableArray(states, flags);
3220
3221        }
3222
3223        public static final Parcelable.Creator<SavedState> CREATOR =
3224                new ClassLoaderCreator<SavedState>() {
3225                    @Override
3226                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
3227                        return new SavedState(in, loader);
3228                    }
3229
3230                    @Override
3231                    public SavedState createFromParcel(Parcel in) {
3232                        return new SavedState(in, null);
3233                    }
3234
3235                    @Override
3236                    public SavedState[] newArray(int size) {
3237                        return new SavedState[size];
3238                    }
3239                };
3240    }
3241}
3242