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