深入理解CoordinatorLayout.Behavior

要研究的几个问题

一、Behavior是什么?为什么要用Behavior?

二、怎么使用Behavior?

三、从源码角度看为什么要这么使用Behavior?

一、Behavior是什么?为什么要用Behavior?

CoordinatorLayout是android support design推出的新布局,主要用于作为视图根布局以及协调子控件的行为,而Behavior就是用于直接子控件来协调自身CoordinatorLayout以及和其他子控件的关系,使用Behavior的控件必须是直接从属于CoordinatorLayout。

在传统的事件分发流程中,在子控件处理事件过程中,父控件是可以进行拦截的,但一旦父控件进行拦截,那么这次事件只能由父控件处理,而不能再由子控件处理了。

在android5.0之后新的嵌套滑动机制中,引入了:NestScrollChildNestedScrollingParent两个接口,用于协调子父控件滑动状态,而CoordinatorLayout实现了NestedScrollingParent接口,在实现了NestScrollChild这个接口的子控件在滑动时会调用NestedScrollingParent接口的相关方法,将事件发给父控件,由父控件决定是否消费当前事件,在CoordinatorLayout实现的NestedScrollingParent相关方法中会调用Behavior内部的方法。

我们实现Behavior的方法,就可以嵌入整个CoordinatorLayout所构造的嵌套滑动机制中,可以获取到两个方面的内容:

1、某个view监听另一个view的状态变化,例如大小、位置、显示状态等

需要重写layoutDependsOnonDependentViewChanged方法

2、某个view监听CoordinatorLayout内NestedScrollingChild的接口实现类的滑动状态

重写onStartNestedScrollonNestedPreScroll方法。注意:是监听实现了NestedScrollingChild的接口实现类的滑动状态,这就可以解释为什么不能用ScrollView而用NestScrollView来滑动了。

二、怎么使用Behavior?

我们先看下Behavior最常见的几个方法,Behavior还有其他比如onMeasureChild、onLayoutChild等一些方法,列举的这几个方法平时还是比较常见的,知道常见方法的使用后,在研究下其他方法,思路还是相通的。

public static abstract class Behavior<V extends View> {
//指定Behavior关注的滑动方向
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
                V child, View directTargetChild, View target, int nestedScrollAxes) {
            return false;
        }
//用来监听滑动状态,对象消费滚动距离前回调
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
                int dx, int dy, int[] consumed) {
            // TODO
        }
//确定子视图与同级视图的依赖
    @Override
     public boolean layoutDependsOn(CoordinatorLayout parent, View
child, View dependency) {
        return Build.VERSION.SDK_INT >= 11 && dependency instanceof Snackbar.SnackbarLayout;
}
 //依赖布局变化时调用
//If the Behavior changes the child view‘s size or position,
//it should return true. The default implementation returns false
    public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
            return false;
        }
    @Override
      public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float
      velocityY, boolean consumed) {
    //快速滑动
       return super.onNestedFling(coordinatorLayout, child,target,velocityX, velocityY, consumed);
}
//所有Behavior能在子View之前收到CoordinatorLayout的所有触摸事件
    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent,View child, MotionEvent ev) {
      return super.onInterceptTouchEvent(parent, child, ev);
    }
  @Override
  public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
      return super.onTouchEvent(parent, child, ev);
    }
}

1、某个view监听另一个view的状态变化

这样的效果最常见的如之后导航栏那样:

前面已经说了,如果要监听另一个view的状态变化,需要重写layoutDependsOnonDependentViewChanged方法,看下具体实现:

layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
                                                 xmlns:app="http://schemas.android.com/apk/res-auto"
                                                 android:id="@+id/behavior_demo_coordinatorLayout"
                                                 android:layout_width="match_parent"
                                                 android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways|snap"
            android:background="?attr/colorPrimary" />
    </android.support.design.widget.AppBarLayout>

        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                android:layout_width="match_parent"
                android:layout_height="400dp"
                android:text="哈哈哈"
                android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:text="哈哈哈"
                    android:gravity="center"/>

            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimary"
        android:gravity="center"
        app:aucher_id="@id/appbar"
        app:layout_behavior="com.mrzk.newstudy.behavior.MyCustomBehavior">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#ffffff"
            android:text="底部导航栏"/>
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

attrs:

   <declare-styleable name="MyCustomStyle">
        <attr name="anchor_id" format="integer|reference"/>
    </declare-styleable>

MyCustomBehavior.java:

public class MyCustomBehavior extends CoordinatorLayout.Behavior<View>{

    private int id;
    public MyCustomBehavior(Context context, AttributeSet attrs) {
        super(context,attrs);
        TypedArray typedArray = context.getResources().obtainAttributes(attrs, R.styleable.MyCustomStyle);
        id = typedArray.getResourceId(R.styleable.MyCustomStyle_anchor_id, -1);
        typedArray.recycle();
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {

//        return dependency instanceof AppBarLayout;
        return dependency.getId() == id;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {

        child.setTranslationY(-dependency.getTop());
        return true;
    }
}

重点关注几点:

首先,我们必须重写两个参数的构造方法,因为通过反射实例化的时候就是用的这个构造方法,在这个构造方法中我们也可以获取一些东西,比如我们的依赖控件ID。

之后layoutDependsOn方法我们来决定要依赖哪个view,如果我们知道要依赖的控件,可以直接写:

return dependency instanceof AppBarLayout

而如果我们不知道,也可以由外部传入,在构造方法中获取资源ID来进行判断,这样具有更高的灵活性:

return dependency.getId() == id

我们看下在CoordinatorLayout中两个方法的调用过程:

class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
        @Override
        public boolean onPreDraw() {
            dispatchOnDependentViewChanged(false);
            return true;
        }
    }

void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
        final int layoutDirection = ViewCompat.getLayoutDirection(this);
        ...
            // Update any behavior-dependent views for the change
            for (int j = i + 1; j < childCount; j++) {
                final View checkChild = mDependencySortedChildren.get(j);
                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
                final Behavior b = checkLp.getBehavior();
              //如果Behavior不为null,layoutDependsOn方法返回true
                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
                    if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) {
                        // If this is not from a nested scroll and we have already been changed
                        // from a nested scroll, skip the dispatch and reset the flag
                        checkLp.resetChangedAfterNestedScroll();
                        continue;
                    }
                  //调用onDependentViewChanged方法
                    final boolean handled = b.onDependentViewChanged(this, checkChild, child);
                 ...
            }
        }
    }

从调用上来看,在CoordinatorLayout内部的任何子view均可产生依赖关系。

2、某个view监听CoordinatorLayout内NestedScrollingChild的接口实现类的滑动状态

如前所说,重写onStartNestedScrollonNestedPreScroll方法。它可以监听实现了NestedScrollingChild的接口实现类的滑动状态

如果用WebView来滚动的,结果预期要隐藏和显示的appbar没有反应,在外层加上NestScrollView就解决了问题,这是因为WebView没有实现NestedScrollingChild接口造成的,因为滑动控件的滑动状态是通过NestedScrollingChild接口方法处理中来调用NestedScrollingParent接口方法来实现。

实现上面的效果我们还可以用重写onStartNestedScrollonNestedPreScroll来实现。来看看吧:

public class MyCustomBehavior extends CoordinatorLayout.Behavior<View>{

    private boolean isAnimate;
    public MyCustomBehavior(Context context, AttributeSet attrs) {
        super(context,attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL)!=-1;//判断是否为垂直滚动
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        //super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);

        if (dy>0 &&!isAnimate && child.getTranslationY()<child.getHeight()){
            child.setTranslationY(child.getTranslationY() + dy);
        }else if (dy<0 &&!isAnimate && child.getTranslationY()>0){
            child.setVisibility(View.VISIBLE);
            if (child.getTranslationY()+dy<0){
                child.setTranslationY(0);
            }else {
                child.setTranslationY(child.getTranslationY()+dy);
            }
        }
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
        //super.onStopNestedScroll(coordinatorLayout, child, target);
            if (child.getTranslationY()<child.getHeight()/2){
                changeState(child,0);
            }else{
                changeState(child,child.getHeight());
            }
    }

    private void changeState(final View view, final int scrollY) {
        ViewPropertyAnimator animator = view.animate().translationY(scrollY).setInterpolator(new FastOutSlowInInterpolator()).setDuration(200*scrollY/view.getHeight());
        animator.setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isAnimate=true;
            }
            @Override
            public void onAnimationEnd(Animator animator) {
                if (view.getTranslationY() == view.getHeight()){
                         view.setVisibility(View.GONE);
                }
                isAnimate=false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                view.setTranslationY(scrollY);
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        animator.start();
    }
}

用这个来实现的话,需要注意的是滚动控件必须实现NestedScrollingChild接口,而没有实现该接口且不调用 dispatchNestedScroll相关接口的滚动控件如ScrollView、WebView、ListView是没有作用的。

三、从源码角度看为什么要这么使用Behavior

我们从Behavior获取实例化开始看,看CoordinatorLayout.LayoutParams源码:

 LayoutParams(Context context, AttributeSet attrs) {
            super(context, attrs);

            final TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.CoordinatorLayout_LayoutParams);

            this.gravity = a.getInteger(
                    R.styleable.CoordinatorLayout_LayoutParams_android_layout_gravity,
                    Gravity.NO_GRAVITY);
            mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_LayoutParams_layout_anchor,
                    View.NO_ID);
            this.anchorGravity = a.getInteger(
                    R.styleable.CoordinatorLayout_LayoutParams_layout_anchorGravity,
                    Gravity.NO_GRAVITY);

            this.keyline = a.getInteger(R.styleable.CoordinatorLayout_LayoutParams_layout_keyline,
                    -1);

            mBehaviorResolved = a.hasValue(
                    R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
            if (mBehaviorResolved) {
              //在这里解析获取Behavior
                mBehavior = parseBehavior(context, attrs, a.getString(
                        R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
            }

            a.recycle();
        }

接着来看看具体是怎么获取到Behavior的:

 static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
            Context.class,
            AttributeSet.class
    };

static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
        if (TextUtils.isEmpty(name)) {
            return null;
        }

        final String fullName;
        if (name.startsWith(".")) {
            // Relative to the app package. Prepend the app package name.
            fullName = context.getPackageName() + name;
        } else if (name.indexOf(‘.‘) >= 0) {
            // Fully qualified package name.
            fullName = name;
        } else {
            // Assume stock behavior in this package (if we have one)
            fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
                    ? (WIDGET_PACKAGE_NAME + ‘.‘ + name)
                    : name;
        }
        try {
            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
            if (constructors == null) {
                constructors = new HashMap<>();
                sConstructors.set(constructors);
            }
            Constructor<Behavior> c = constructors.get(fullName);
            if (c == null) {
                //这里通过反射获取到Behavior
                final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
                        context.getClassLoader());
                //获取两个参数的构造方法
                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
                c.setAccessible(true);
                constructors.put(fullName, c);
            }
          //在这里实例化
            return c.newInstance(context, attrs);
        } catch (Exception e) {
            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
        }
    }

这里就解释了为什么我们每次继承都要写两个参数的构造方法了,如果没有,则会报Caused by: java.lang.NoSuchMethodException: [class android.content.Context, interface android.util.AttributeSet]错误。

然后我们看看主要关注的onStartNestedScroll和onNestedPreScroll的调用时机,当实现了NestScrollChild接口的子控件滑动时,会回调CoordinatorLayout中的onStartNestedScroll方法:

 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        boolean handled = false;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            //获取Behavior
            final Behavior viewBehavior = lp.getBehavior();
            if (viewBehavior != null) {
            //true if the Behavior wishes to accept this nested scroll
            //调用viewBehavior.onStartNestedScroll方法,如果返回true表示希望接受滚动事件
                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
                        nestedScrollAxes);
                handled |= accepted;
                lp.acceptNestedScroll(accepted);
            } else {
                lp.acceptNestedScroll(false);
            }
        }
        return handled;
    }

当实现了NestScrollChild接口的子控件滚动时,在消费滚动距离之前把总的滑动距离传给父布局,即CoordinatorLayout。然后回调onNestedPreScroll方法:

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        int xConsumed = 0;
        int yConsumed = 0;
        boolean accepted = false;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            //遍历所有子控件 如果不希望接受处理事件  跳出本次循环
            if (!lp.isNestedScrollAccepted()) {
                continue;
            }
            //获得child view的Behavior
            final Behavior viewBehavior = lp.getBehavior();
            if (viewBehavior != null) {
                mTempIntPair[0] = mTempIntPair[1] = 0;
                //调用viewBehavior.onNestedPreScroll方法
                viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair);
                //dy大于0是向上滚动 小于0是向下滚动
                xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
                        : Math.min(xConsumed, mTempIntPair[0]);
                yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
                        : Math.min(yConsumed, mTempIntPair[1]);

                accepted = true;
            }
        }
        //consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离
        consumed[0] = xConsumed;
        consumed[1] = yConsumed;

        if (accepted) {
            dispatchOnDependentViewChanged(true);
        }
    }

然后我们来研究layoutDependsOn和onDependentViewChanged的调用时机,看CoordinatorLayout的dispatchOnDependentViewChanged方法:

void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
        final int layoutDirection = ViewCompat.getLayoutDirection(this);
        final int childCount = mDependencySortedChildren.size();
        for (int i = 0; i < childCount; i++) {
            final View child = mDependencySortedChildren.get(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // Check child views before for anchor
            for (int j = 0; j < i; j++) {
                final View checkChild = mDependencySortedChildren.get(j);

                if (lp.mAnchorDirectChild == checkChild) {
                    offsetChildToAnchor(child, layoutDirection);
                }
            }

            // Did it change? if not continue
            final Rect oldRect = mTempRect1;
            final Rect newRect = mTempRect2;
            getLastChildRect(child, oldRect);
            getChildRect(child, true, newRect);
            if (oldRect.equals(newRect)) {
                continue;
            }
            recordLastChildRect(child, newRect);

            // Update any behavior-dependent views for the change
            for (int j = i + 1; j < childCount; j++) {
                final View checkChild = mDependencySortedChildren.get(j);
                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
                final Behavior b = checkLp.getBehavior();
                //behavior不为null同时layoutDependsOn返回了true
                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
                    if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) {
                        // If this is not from a nested scroll and we have already been changed
                        // from a nested scroll, skip the dispatch and reset the flag
                        checkLp.resetChangedAfterNestedScroll();
                        continue;
                    }
                    //this:CoordinatorLayout
                    //checkChild:behavior所属的view
                    //child:依赖的view
                    //true if the Behavior changed the child view‘s size or position, false otherwise
                    final boolean handled = b.onDependentViewChanged(this, checkChild, child);

                    if (fromNestedScroll) {
                        // If this is from a nested scroll, set the flag so that we may skip
                        // any resulting onPreDraw dispatch (if needed)
                        checkLp.setChangedAfterNestedScroll(handled);
                    }
                }
            }
        }
    }

这段代码在onNestedScroll、onNestedPreScroll、onNestedFling和OnPreDrawListener.onPreDraw方法中都有调用,判断依赖控件大小或者位置变化时及时通知behavior,子控件作出相应调整。

这里把我们主要关心的控件的调用时机大体走读了一遍,对于为什么在behavior中调用相关方法可以依赖和监听其他控件的滑动事件应该有了一定认识,如果关注CoordinatorLayout的实现细节,务必要搞明白NestScrollChild和NestedScrollingParent机制的调用关系,建议查看NestScrollView源码,这里给出NestScrollChild和NestedScrollingParent的一些主要方法说明,对其具体了解还可以看Android 嵌套滑动机制(NestedScrolling)这篇文章。

NestScrollChild

public void setNestedScrollingEnabled(boolean enabled)

enabled:true表示view使用嵌套滚动,false表示禁用

public boolean startNestedScroll(int axes)

axes:表示滚动的方向如:ViewCompat.SCROLL_AXIS_VERTICAL(垂直方向滚动)和 ViewCompat.SCROLL_AXIS_HORIZONTAL(水平方向滚动)

return:true表示本次滚动支持嵌套滚动,false不支持

startNestedScroll表示view开始滚动了,一般是在ACTION_DOWN中调用,如果返回true则表示父布局支持嵌套滚动

public void stopNestedScroll()

在事件结束比如ACTION_UP或者ACTION_CANCLE中调用stopNestedScroll,告诉父布局滚动结束

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)

dxConsumed: 表示view消费了x方向的距离长度

dyConsumed: 表示view消费了y方向的距离长度

dxUnconsumed: 表示滚动产生的x滚动距离还剩下多少没有消费>dyUnconsumed: 表示滚动产生的y滚动距离还剩下多少没有消费

offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少

在view消费滚动距离之后,把剩下的滑动距离再次传给父布局

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)

dx: 表示view本次x方向的滚动的总距离长度

dy: 表示view本次y方向的滚动的总距离长度

consumed: 表示父布局消费的距离,consumed[0]表示x方向,consumed[1]表示y方向

参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少

view消费滚动距离之前把总的滑动距离传给父布局

* public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed)*

velocityX:X方向滚动的距离

velocityY:Y方向滚动的距离

consumed:父布局是否消费

public boolean dispatchNestedPreFling(float velocityX, float velocityY)

velocityX:X方向滚动的距离

velocityY:Y方向滚动的距离

NestedScrollingParent

public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)

child:ViewParent包含触发嵌套滚动的view的对象

target:触发嵌套滚动的view (在这里如果不涉及多层嵌套的话,child和target)是相同的

nestedScrollAxes:就是嵌套滚动的滚动方向了.

当子view的调用NestedScrollingChild的方法startNestedScroll时,会调用该方法

该方法决定了当前控件是否能接收到其内部View(并非是直接子View)滑动时的参数

public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);

如果onStartNestedScroll方法返回true,之后就会调用该方法.它是让嵌套滚动在开始滚动之前,让布局容器(viewGroup)或者它的父类执行一些配置的初始化(React to the successful claiming of a nested scroll operation)

public void onStopNestedScroll(View target)

当子view调用stopNestedScroll时会调用该方法,停止滚动

public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)

target:同上

dxConsumed:表示target已经消费的x方向的距离

dyConsumed:表示target已经消费的x方向的距离

dxUnconsumed:表示x方向剩下的滑动距离

dyUnconsumed:表示y方向剩下的滑动距离

当子view调用dispatchNestedScroll方法时,会调用该方法

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)

target:同上

dx:表示target本次滚动产生的x方向的滚动总距离

dy:表示target本次滚动产生的y方向的滚动总距离

consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.

当子view调用dispatchNestedPreScroll方法是,会调用该方法

调用时机:

子view 父view
startNestedScroll onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll
时间: 2024-10-16 22:27:03

深入理解CoordinatorLayout.Behavior的相关文章

CoordinatorLayout高级用法-自定义Behavior

在新的support design中,CoordinatorLayout可以说是最重要的一个控件了,CoordinatorLayout给我们带来了一种新的事件的处理方式--behavior,你是不是还记得我们在使用CoordinatorLayout的时候,一些子view需要一段, app:layout_behavior="@string/appbar_scrolling_view_behavior" 这样的xml配置?当时我们不知道这是干嘛的,直接照用就行了,后来发现这玩意是一个类!而

【知识必备】一文让你搞懂design设计的CoordinatorLayout和AppbarLayout联动,让Design设计更简单~

一.写在前面 其实博主在之前已经对design包的各个控件都做了博文说明,无奈个人觉得理解不够深入,所以有了这篇更加深入的介绍,希望各位看官拍砖~ 二.从是什么开始 1.首先我们得知道CoordinatorLayout是什么玩意儿,到底有什么用,我们不妨看看官方文档的描述: CoordinatorLayout是一个"加强版"FrameLayout,它主要有两个用途: 1.用作应用的顶层布局管理器,也就是作为用户界面中所有UI控件的容器 2.用作相互之间具有特定交互行为的UI控件的容器

重要的ui组件——Behavior

v7包下的组件类似CoordinatorLayout推出也有一段时间了,大家使用的时候应该会体会到其中很多的便利,今天这篇文章带大家来了解一个比较重要的ui组件——Behavior.从字面意思上就可以看出它的作用,就是用来规定某些组件的行为的,那它到底是什么,又该怎么用呢?看完这篇文章希望大家会有自己的收获- 前言 写这篇文章的起因是因为我无意中在GitHub上发现了Jake Wharton大神新建了一个Repo,内容是JakeWharton/DrawerBehavior.有兴趣的同学可以去看看

掌握CoordinatorLayout

原文链接 Mastering the Coordinator Layout 在Google I/0 2015大会上,Google发布了一个与Material Design相关的控件支持库,在这个控件库中我们可以找到很多新的ViewGroup控件,例如 AppBarLayout,CollapsingToolbarLayout和CoordinatorLayout. 考虑到这些新的控件非常有用,因此我决定写一篇文章来介绍其相关的配置和使用技巧. CoordinatorLayout 这个控件的作用和它的

Android Design Support Library--FloatingActionButton及其Behavior的使用

引言 如果说前面提到的TextInputLayout.SnackBar的应用还不是很常见的话,那么今天提到的FloatingActionButton绝对是一个随处可见的Material Design控件了,无论是我们常用的知乎.印象笔记或者是可爱的谷歌全家桶套装都可以见到FloatingActionButton的身影,今天就来说说FloatingActionButton. 关于使用 其实我相信很多人都用过了Material Design控件了,但是还是要说一下,毕竟有些人接触的晚一些,一些人接触

JuheNews系列之二 &#183; ToolBar+AppBarLayout+CoordinatorLayout+CollapsingToolbarLayout+NestedScrollView

JuheNews系列之二 · ToolBar+AppBarLayout+CoordinatorLayout+CollapsingToolbarLayout Android5.0重要的改变之一,除了Material Design风格的设计思想,应该就是沉浸式状态栏了吧.之前实现沉浸式的效果时,都是借助SystemBarTint来实现,或许以后你也可以丢弃这种方式了. 除此之外,由于actionbar不够灵活,项目中我一般喜欢把Activity的ActionBar去掉,然后自己在布局中写一个公用的H

Android M —— CoordinatorLayout

英文原文:https://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout 这篇文章专门讲解和CoordinatorLayout相关的知识点,这也是Design Support Library中最重要与最难的部分. 概览 CoordinatorLayout 实现了多种Material Design中提到的滚动效果.目前这个框架提供了几种不用写动画代码就能工作的方法,这些效果包括: 让浮动操作按钮上下滑动,为Sn

Android CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、Toolbar 等的说明和使用

请尊重个人劳动成果,转载注明出处,谢谢! http://blog.csdn.net/amazing7/article/details/51918623 1. CoordinatorLayout 我们通常把CoordinatorLayout作为顶层布局来协调其子布局之间的动画效果. 子view1在布局中通过设置behavior属性与子view2关联,当移动view2的时候view1产生相应的效果,而这个效果具体是怎么样的由behavior类来决定.我们把view1叫做Child,view2叫做De

CoordinatorLayout翻译

CoordinatorLayout https://developer.android.google.cn/reference/android/support/design/widget/CoordinatorLayout.html public class CoordinatorLayoutextends ViewGroup implements NestedScrollingParent java.lang.Object   ?android.view.View      ?android.