nestedscrollparent/children

package com.vortex.scrolltest;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.OverScroller;
import android.widget.TextView;

/**
 * Created by Administrator (chenPS) on 2019/7/29.
 */

public class MyNestedScrollParent extends LinearLayout implements NestedScrollingParent {
    private ImageView iv;
    private TextView tv;
    private MyNestedScrollChild mMyNestedScrollChild;

    private NestedScrollingParentHelper mParentHelper;
    private OverScroller mScroller;

    private int ivHeight;
    private int tvHeight;

    public MyNestedScrollParent(Context context) {
        super(context);
    }

    public MyNestedScrollParent(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mParentHelper = new NestedScrollingParentHelper(this);
        mScroller = new OverScroller(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        iv = (ImageView) getChildAt(0);
        tv = (TextView) getChildAt(1);
        mMyNestedScrollChild = (MyNestedScrollChild) getChildAt(2);
        iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (ivHeight <= 0) {
                    ivHeight = iv.getMeasuredHeight();
                }
            }
        });
        tv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (tvHeight <= 0) {
                    tvHeight = tv.getMeasuredHeight();
                }
            }
        });
    }

    /**
     *
     * @param dy  滚动距离 大于0 是网上滚   逐渐显示图片
     * @return  getscrollY的值是逐渐减小的。一旦为0说明滑到顶部了
     */
    private boolean showIv(int dy) {
        if (dy > 0) {
            Log.v("test",getScrollY()+"   showIv");
            if (getScrollY() > 0 && mMyNestedScrollChild.getScrollY() == 0) {
                return true;
            }
        }
        return false;
    }
    /**
     *
     * @param dy  滚动距离 小于0 是向下滑滚 逐渐隐藏图片
     * @return  getscrollY是父的滑动距离  一旦滑动到iv的高度  说明 已经完全不见了,就不需要在滑动父布局了
     * getscrolly 的值是逐渐增大的
     */
    private boolean hideIv(int dy) {
        if (dy < 0) {
            Log.v("test",getScrollY()+"   showIv");
            if (getScrollY() < ivHeight) {
                return true;
            }
        }
        return false;
    }


    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        if (target instanceof MyNestedScrollChild) {
            return true;
        }
        return false;
    }

    private void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, ivHeight);
        invalidate();
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        mParentHelper.onNestedScrollAccepted(child, target, axes);
    }

    @Override
    public void onStopNestedScroll(View child) {
        mParentHelper.onStopNestedScroll(child);
    }

    //先与 child 滚动

    /**
     *  在显示 隐藏图片的过程中  也代表这个时候 需要显示 /隐藏的是 父布局的移动效果
     *  不需要去操纵 子布局的滑动
     *  下滑 dy小于0  上滑dy大于0
     */
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        if (showIv(dy) || hideIv(dy)) {
            Log.v("test", "onNestedPreScroll  先于child滚动" + dy);
            scrollBy(0, -dy);
            consumed[1] = dy;
        }
    }

    // child滚动后
    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        Log.d("test","onNestedScroll"+dyUnconsumed);
        if (dyUnconsumed > 0) {
            scrollBy(0, -dyUnconsumed);
        }
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        if (getScrollY() >= 0 && getScrollY() < ivHeight) {
            fling((int) velocityY);
            return true;
        }
        return false;
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        if (!consumed) {
            fling((int) velocityX);
            return true;
        }
        return false;
    }

    @Override
    public int getNestedScrollAxes() {
        return mParentHelper.getNestedScrollAxes();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > ivHeight) {
            y = ivHeight;
        }

        super.scrollTo(x, y);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }


    private int lastY;
    private VelocityTracker mVelocityTracker;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = (int) event.getRawY();
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int y = (int) event.getRawY();
                int dy = y - lastY;
                lastY = y;
                scrollTo(0, -dy);
                break;
            case MotionEvent.ACTION_UP:
                mVelocityTracker.computeCurrentVelocity(1000);
                int vy = (int) mVelocityTracker.getYVelocity();
                fling(-vy);
                break;
        }


        return true;
    }
}
package com.vortex.scrolltest;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.OverScroller;

/**
 * Created by Administrator (chenPS) on 2019/7/29.
 */

public class MyNestedScrollChild extends LinearLayout implements NestedScrollingChild {

    private NestedScrollingChildHelper mChildHelper;
    private OverScroller mScroller;
    private int maxScrollY;
    private VelocityTracker mVelocityTracker;
    private int lastY;
    private final int[] offset = new int[2]; //偏移量
    private final int[] consumed = new int[2]; //消费

    public MyNestedScrollChild(Context context) {
        super(context);
    }

    public MyNestedScrollChild(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
        mScroller = new OverScroller(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int contentHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);
            contentHeight += view.getMeasuredHeight();
        }
        int parentHeight = ((ViewGroup) getParent()).getMeasuredHeight();
        int pinTopHeight = (int) (getResources().getDisplayMetrics().density * 50 + 0.5); //50dp转 对应的像素  固定头的高度
        int visibleHieght = parentHeight - pinTopHeight;
        maxScrollY = contentHeight - visibleHieght;
        setMeasuredDimension(getMeasuredWidth(), visibleHieght);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = (int) event.getRawY();
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int y = (int) event.getRawY();
                int dy = y - lastY;
                lastY = y;

                /**
                 *  public boolean startNestedScroll(int axes);

                   开启嵌套滚动流程(实际上是进行了一些嵌套滚动前准备工作)。
                   当找到了能够配合当前子view进行嵌套滚动的父view时,返回值为true
                 (Returns:true if a cooperative parent was found and nested scrolling
                 has been enabled for the current gesture)。

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

                   在子view自己进行滚动之前调用此方法,询问父view是否要在子view之前进行滚动。
                   此方法的前两个参数用于告诉父View此次要滚动的距离;而第三第四个参数用于子view获取父view消费掉的距离和父view位置的偏移量。
                   第一第二个参数为输入参数,即常规的函数参数,调用函数的时候我们需要为其传递确切的值。而第三第四个参数为输出参数,调用函数时我们只需要传递容器(在这里就是两个数组),在调用结束后,我们就可以从容器中获取函数输出的值。
                   如果parent消费了一部分或全部距离,则此方法返回true。
                 */
                if (startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) &&   //找到了配合滑动的父view
                        dispatchNestedPreScroll(0, dy, consumed, offset)) {  //父类消费距离

                    /**
                     * 猜测下 这两个数组 第一位表示 x 第二位表示y值
                     */

                    int remain = dy - consumed[1];

                    if (remain != 0) {
                        scrollBy(0, -remain);
                    }
                } else {
                    scrollBy(0, -dy);
                }
                break;
            case MotionEvent.ACTION_UP:
                mVelocityTracker.computeCurrentVelocity(1000);
                float vy = mVelocityTracker.getYVelocity();
                if (!dispatchNestedPreFling(0, -vy)) { // 如果父类没有消费滑动事件  自己滑动
                    fling(-vy);
                }
                break;

        }
        return true;
    }

    private void fling(float velocityY) {
        mScroller.fling(0, getScrollY(), 0, (int) velocityY, 0, 0, 0, maxScrollY);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y > maxScrollY) {
            y = maxScrollY;
        }
        if (y < 0) {
            y = 0;
        }
        super.scrollTo(x, y);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {  //如果还在滑动
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }


    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);

    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }
}

笔记
这里有个bug 上滑 会有个白色的底部空白。暂时放下

参考文章
https://blog.csdn.net/mchenys/article/details/80041618

你可能感兴趣的:(nestedscrollparent/children)