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