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