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