美文网首页控件
CoordinatorLayout的使用总结

CoordinatorLayout的使用总结

作者: 陈文超happylion | 来源:发表于2018-01-04 17:31 被阅读215次

    CoordinatorLayout中的子组件如果不添加相应的依赖关系,那么CoordinatorLayout表现就是一个frameLayout。

    CoordinatorLayout不是必须要有NestedScrollingChild。

    CoordinatorLayout 本身是不具有滑动功能的。

    特性1:子组件靠behavior建立依赖关系

    CoordinatorLayout中如果想要实现A子组件始终在B组件的上面,这里的上指的是页面的上下,不是重叠的上下。(因为如果什么都不添加,表现就像frameLayout的子组件的一样:相互重叠),那么就需要A组件或者B组件添加依赖关系,注意,依赖的组件只能是CoordinatorLayout的顶级组件,也就是需要A组件或者B组件添加一个Behavior,即重写layoutDependsOn和onDependentViewChanged两个方法。比如:

    public class FollowBehavior extends CoordinatorLayout.Behavior {
      private int targetId;
    
    //从xml中找到添加的需要依赖组件的那个id。
      public FollowBehavior(Context context, AttributeSet attrs) {
          super(context, attrs);
          TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Follow);
          for (int i = 0; i < a.getIndexCount(); i++) {
              int attr = a.getIndex(i);
              if(a.getIndex(i) == R.styleable.Follow_target){
                  targetId = a.getResourceId(attr, -1);
              }
          }
          a.recycle();
      }
    
      @Override// 当依赖的那个view改变时,回调通知这个behavior对应的view应该怎么做.  child 参数就是那个bevior对应的view,也就是你把这个behavior赋予给的那个view。
      public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
           child.setY(dependency.getY()+dependency.getHeight());
          return true;
      }
    
      @Override// 说明这个behavior对应的view依赖的是哪个view 
      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
          return dependency.getId() == targetId;
      }
    }
    

    子组件靠behavior监听NestedScrollingChild的滑动

    首先要注意,子组件并不在NestedScrollingChild里面(即手指触摸的是NestedScrollingChild,没有触摸子组件,但是子组件却能监听手指的滑动,进而做出滑动等相应)。对于多个子组件都设置了bebavior,那么这些子组件都会监听NestedScrollingChild的滑动。还需要注意,如果手指触摸到子组件上滑动,没有在NestedScrollingChild滑动,如果子组件没有实现NestedScrollingChild,那么这时候整个页面是不会动的。所以,如果手指在coordinateLayout的子组件上滑动时,如果想整个布局也滑动,需要这个子组件也实现NestedScrollingChild。为什么呢?因为实现以后,就会把滑动事件交给coordinateLayout,然后coordinateLayout再把事件交给带behavior的子组件。你可能会说,这样的话,coordinateLayout岂不是有两个NestedScrollingChild。可以有两个NestedScrollingChild。下面会说一个view怎么实现NestedScrollingChild。

    behavior除了可以建立CoordinatorLayout顶级子组件的依赖关系外(layoutDependsOn和onDependentViewChanged两个方法),顶级子组件还可以使用behavior监听NestedScrollingChild实现类的滑动,当然,需要CoordinatorLayout中有NestedScrollingChild。 behavior监听NestedScrollingChild的滑动,依靠的是bahavior的三个方法(不需要在bahavior中建立依赖就能监听):

    //这里返回true,NestedScrollingChild才会给该behvior对应的view分发事件。
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes);
    
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
    
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
    

    当我们滚动页面时,NestedScrollingChild接收到了事件,知道了手指在x 和y方向滑动的距离,然后NestedScrollingChild会把这个事件通过

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

    方法分发给CoordinatorLayout,继而分发给behavior,即,调用behavior onNestedPreScroll()方法。dispatchNestedPreScroll()中的dx就是这次操作,手指在x方向的距离。那么consumed数组是什么呢?注解上说了,这是一个Output 数组,也就是说会传入一个空数组。bahavior的onNestedPreScroll()方法拿到这个空数组后,根据onNestedPreScroll()的逻辑,即在x,y方向消费了多少,把消费的数值传到consumed数组中。这样,NestedScrollingChild就知道NestedScrollingParent消费了多少了。然后NestedScrollingChild会将手指在x方向的距离减去NestedScrollingParent在x方向消费的距离得到这次操作剩余的距离(即NestedScrollingParent可能没消费完全),然后NestedScrollingChild会自己消费掉剩下的距离。当然,如果,behavior中没有重写onNestedPreScroll()方法,那么dispatchNestedPreScroll()会返回false,意味着NestedScrollingChild会消费掉这次操作手指滑动的所有距离。
    等到NestedScrollingChild消费掉它应该消费的距离后,会调用

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

    通知一下CoordinatorLayout,继而通知behavior,即,调用behavior的onNestedScroll()方法。dispatchNestedScroll()中的dxConsumed为NestedScrollingChild在x方向已经消费的距离。这个参数和behavior的onNestedScroll()中的dxConsumed参数是一样的,都是表示NestedScrollingChild在x方向已经消费的距离。这样behavior就可以根据这个距离来让behavior对应的view是不是也消费相同的距离来协同滑动,或其他数值的距离。

    所以,behavior中重写onNestedPreScroll()方法,可以设置一些条件,在某些条件下,让手指的滑动距离被behavior对应的view消费完全,NestedScrollingChild就不再参与消费了

    behavior中重写onNestedScroll()方法,记住,回调behavior此方法时,NestedScrollingChild已经完成了滑动距离的消费。所以,使用这个方法可以让behavior对应的view也滑动某些距离和NestedScrollingChild保证协同滑动。

    coordinateLayout 中搭配viewPager

    coordinateLayout中含有viewPager,和一些其他带behavior的顶级子view。如果viewPager中含有NestedScrollingChild,那么coordinateLayout中的顶级子view是可以接受到NestedScrollingChild分发的滑动事件的。

    不过,对于flinger滑动可能有些问题,可以参考:

    http://blog.csdn.net/bigggfish/article/details/53585783

    一个view怎么实现NestedScrollingChild

    可以看这个库 的NestedLinearLayout类。
    第一步,让这个view实现NestedScrollingChild的接口。很容易实现,都是调用的NestedScrollingChildHelper的工具方法。

    第二步,如果手指在这个view上滑动,怎么把滑动事件交给coordinateLayout处理。事件来到这个view了,我们需要调用onInterceptTouchEvent()拦截MOVE事件(注意虽然在onInterceptTouchEvent不让down事件返回true,但是需要在down事件到来时,调用NestedScrollingChild接口方法startNestedScroll
    (),第一步实现好了,这个方法的作用就是看看coordinateLayout的各个bahavior有没有能处理这个动作的,即bahavior的onStartNestedScroll
    是不是返回true)。我们知道拦截了MOVE,之后这个手势动作剩下的move和up事件就不会往下传了,而是马上会回调该view的onTouchEvent()方法,处理MOVE。怎么处理呢?很简单,我们只需要调用dispatchNestedPreScroll()将该事件分发到coordinateLayout就可以了。coordinateLayout 会分发给各个带bebavior的
    子view。当然可能当下的这个view就实现了behavior接口,即该view的bahavior会处理这个move动作。dispatchNestedPreScroll()这个我们在第一步时已经实现了,所以直接拿来用即可。

    参考 :这个库
    这个

    相关文章

      网友评论

        本文标题:CoordinatorLayout的使用总结

        本文链接:https://www.haomeiwen.com/subject/qetdnxtx.html