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