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