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