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