美文网首页
NestedScrolling机制的学习笔记(二)

NestedScrolling机制的学习笔记(二)

作者: 陈添 | 来源:发表于2017-03-26 10:56 被阅读106次

    这篇文章会用NestedScrolling机制做一个实例,此实例代码参考自:http://blog.csdn.net/al4fun/article/details/53889075

    我在源代码的基础上,删减了些代码,加了些注释

    例子效果如下: 1.gif

    先贴出布局文件代码:

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.nesteddemo.MyParent xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.nesteddemo.MainActivity"
        android:orientation="vertical">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher_round"
            android:layout_gravity="center_horizontal"/>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="我是不会隐藏的文字栏,辣鸡"
            android:background="#FFB6C1"
            android:textSize="25sp"/>
    
        <com.example.nesteddemo.MyChild
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/libai"
                android:textSize="25sp"/>
    
        </com.example.nesteddemo.MyChild>
    
    </com.example.nesteddemo.MyParent>
    

    布局文件很简单,其中MyParent和MyChild分别是实现了NestedScrollingParent和NestedScrollingChild接口的父容器和子元素,这两个View都继承自LinearLayout。

    我们先来看看MyChild是怎样实现的:

    public class MyChild extends LinearLayout implements NestedScrollingChild {
        NestedScrollingChildHelper nscp;
        int lastY;
    
        //这两个数组用来接收父容器传过来的参数
        int[] consumed;
        int[] offsetWindow;
    
        int showHeight;
    
        public MyChild(Context context) {
            this(context,null);
        }
    
        public MyChild(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //第一次测量,因为是wrap_content,测量出来的只是父容器除了ImageView和TextView剩余的高度
            //此次测量只是为了求得剩余的高度
            //如果没有第二次测量,那么下面的文字就会显示不出来
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            showHeight = getMeasuredHeight();
    
            //现在我们把MeasureSpec设置为UNSPECIFIED,这样MyChild的高度就没有限制了,也就能显示全部的文字了
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
            super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    lastY = (int) event.getRawY();
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    int y = (int) event.getRawY();
                    int dy = y-lastY;
                    lastY = y;
    
                    //开启NestedScrolling机制,如果找到了匹配的父容器,那么就与父容器配合消费掉滑动距离
                    if(startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)){
                        //dy是我们传过去的滑动的距离,父容器可以根据逻辑来选择要不要消费,消费多少
                        dispatchNestedPreScroll(0,dy,consumed,offsetWindow);
                        scrollBy(0,-dy);
                    }
    
                    break;
    
                case MotionEvent.ACTION_UP:
                    break;
            }
    
            return true;
        }
    
        //scrollBy内部调用scrollTo,我们不能滑出去,也不能滑的太下面,我们要修正这些情况
        @Override
        public void scrollTo(@Px int x, @Px int y) {
            int maxY = getMeasuredHeight()-showHeight;
            if(y>maxY){
                y=maxY;
            }
            else if(y<0){
                y=0;
            }
            super.scrollTo(x, y);
        }
    
        //这里使用单例模式提供Helper,我发现如果没有单例模式,机制就会失效
        //原因我大致的猜到了,但是我还不能具体的表达出来,如果有人知道,请在评论区留下言
        private NestedScrollingChildHelper getNscp(){
            if(nscp == null){
                nscp = new NestedScrollingChildHelper(this);
                nscp.setNestedScrollingEnabled(true);
                return nscp;
            }else {
                return nscp;
            }
        }
    
        @Override
        public void setNestedScrollingEnabled(boolean enabled) {
            getNscp().setNestedScrollingEnabled(enabled);
        }
    
        @Override
        public boolean isNestedScrollingEnabled() {
            return getNscp().isNestedScrollingEnabled();
        }
    
        @Override
        public boolean startNestedScroll(int axes) {
            return getNscp().startNestedScroll(axes);
        }
    
        @Override
        public void stopNestedScroll() {
            getNscp().stopNestedScroll();
        }
    
        @Override
        public boolean hasNestedScrollingParent() {
            return getNscp().hasNestedScrollingParent();
        }
    
        @Override
        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
            return getNscp().dispatchNestedScroll(dxConsumed,dyConsumed,dxUnconsumed,dyUnconsumed,offsetInWindow);
        }
    
        @Override
        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
            return getNscp().dispatchNestedPreScroll(dx,dy,consumed,offsetInWindow);
        }
    
        @Override
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
            return getNscp().dispatchNestedFling(velocityX,velocityY,consumed);
        }
    
        @Override
        public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
            return getNscp().dispatchNestedPreFling(velocityX,velocityY);
        }
    }
    

    大多数的函数我们都用帮助类的同名函数处理了,其余的代码我已经写上了详细的注释,不难看懂。

    接下看下MyParent的实现:

    public class MyParent extends LinearLayout implements NestedScrollingParent {
        NestedScrollingParentHelper nsp;
        ImageView iv;
        MyChild nsc;
        int ivHeight;
    
        public MyParent(Context context) {
            this(context,null);
        }
    
        public MyParent(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            //创建一个Helper类
            nsp = new NestedScrollingParentHelper(this);
        }
    
        //拿到父容器里面的三个子View
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            iv = (ImageView) getChildAt(0);
            nsc = (MyChild) getChildAt(2);
    
            //拿到ImageView的高度
            iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    if(ivHeight<=0){
                        ivHeight = iv.getMeasuredHeight();
                    }
                }
            });
    
        }
    
        @Override
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
            //参数里target是实现了NestedScrolling机制的子元素,这个子元素可以不是父容器的直接子元素
            //child是包含了target的View,这个View是父容器的直接子元素
            if(target instanceof MyChild){
                return true;
            }
    
            return false;
        }
    
        @Override
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
            //dy是子View传过来的,来询问父容器是不是要消费他,要的话,就把dy放进consumed数组,表示我消费了
            //其中consumed数组,consumed[0]表示x方向的距离,consumed[1]表示y方向的距离
            if(showImg(dy)||hideImg(dy)/*这里根据业务逻辑来判断*/){
                scrollBy(0,-dy);
                consumed[1] = dy;
            }
        }
    
        private boolean hideImg(int dy) {
            //上拉的时候,判断是不是要隐藏图片
            if(dy<0){
                if(getScrollY()<ivHeight){
                    //判断只要上移的部分,没有超过ImageView,那么就让父容器继续滑动
                    return true;
                }
            }
    
            return false;
        }
    
        private boolean showImg(int dy) {
            //下拉的时候,判断是不是要显示图片
            if(dy>0){
                if(nsc.getScrollY()==0){
                    return true;
                }
            }
    
            return false;
        }
    
        //scrollBy内部调用scrollTo,我们父容器不能滑出去,也不能滑的太下面,我们要修正这些情况
        @Override
        public void scrollTo(@Px int x, @Px int y) {
            if(y>ivHeight){
                y = ivHeight;
            }
            else if(y<0){
                y=0;
            }
    
            super.scrollTo(x,y);
        }
    
        @Override
        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
            nsp.onNestedScrollAccepted(child,target,nestedScrollAxes);
        }
    
        @Override
        public void onStopNestedScroll(View target) {
            nsp.onStopNestedScroll(target);
        }
    
        @Override
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
    
        }
    
        @Override
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
            return false;
        }
    
        @Override
        public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
            return false;
        }
    
        @Override
        public int getNestedScrollAxes() {
            return 0;
        }
    }
    

    和MyChild一样,大多数函数都用帮助类的同名函数处理了,其余的也写上了详细的注释。

    好了,没了。

    完整代码地址:https://github.com/ChenTianSaber/NestedScrollingDemo

    结束:

    这篇文章先到此结束。

    感觉写的好干,因为大多数地方都被帮助类实现了,而且例子也简单,没有什么一步一步的步骤。大家可以看懂了之后,自己写一遍。

    接下来我们会分析一下NestedScrolling的源码,来看看它们是怎么做到相互配合的。

    如果有问题,请在评论区留言,才疏学浅,欢迎大家批评指正。

    最后的最后:

    感谢我可爱的女朋友。

    相关文章

      网友评论

          本文标题:NestedScrolling机制的学习笔记(二)

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