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