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