嵌套滑动的LinearLayout(滑动隐藏头部的LinearLayout),滑动时先隐藏头部(第一个子控件)再滑动recyclerview

需求:LinearLayout内部包含一个头部和一个recyclerview。recyclerview可滚动,滚动时如果头部仍在可见则先滚动LinearLayout直到头部消失,再开始滚动recyclerview。

思路:
1.自定义LinearLayout实现NestedScrollingParent,getChildAt(0)获取头部,计算头部高度。将LinearLayout的高度设置为自身高度 + 头部高度(隐藏头部后仍完整显示recyclerview)。
2.在child滑动时 ,优先进行linearLayout的滑动,待linearLayout滑动到顶部或者向下滑动距离为头部高度时不再消费滑动事件并交由child继续执行。
3.为了使滑动在LinearLayout的任意区域都能滑动LinearLayout,所有child包括头部都需要实现NestedScrollingChild。或者使用NestedSrollView、Recyclerview这些已经实现NestedScrollingChild的控件。
4.仅实现滑动事件在filing时会有卡顿,为了提升用户体验应当将filing的相关方法实现。

实现:

public class HideFirstViewNestedLinearLayout extends LinearLayout implements NestedScrollingParent {

    private View mHeader;
    private int mHeaderHeight;
    private boolean mIsNeedNestedScroll = false;
    private Scroller mScroller = new Scroller(getContext());

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

    public HideFirstViewNestedLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public HideFirstViewNestedLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(target, dx, dy, consumed);
        //过滤横向的误操作
        if (Math.abs(dx) * 1.5 > Math.abs(dy)) {
            return;
        }
        //滑动距离不得超过头部高度
        boolean headerScrollUp = dy > 0 && getScrollY() < mHeaderHeight;
        //滑动距离不得小于0
        boolean headerScrollDown = dy < 0 && getScrollY() > 0 && !target.canScrollVertically(-1);
        if ((headerScrollUp || headerScrollDown) && isIsNeedNestedScroll()) {
            if (dy + getScrollY() > mHeaderHeight) {
                dy = mHeaderHeight - getScrollY();
            }
            if (dy + getScrollY() < 0) {
                dy = 0 - getScrollY();
            }
            scrollBy(0, dy);
            consumed[1] = dy;
        }
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        //当垂直方向速率大于头部高度时,开始动画自动滑动到顶部或底部
        if (Math.abs(velocityY) > mHeaderHeight && isIsNeedNestedScroll()) {
            if (velocityY > 0) {
                mScroller.startScroll(getScrollX(), getScrollY(), 0, mHeaderHeight - getScrollY());
            } else {
                mScroller.startScroll(getScrollX(), getScrollY(), 0, 0 - getScrollY());
            }
            invalidate();
        }
        return super.onNestedPreFling(target, velocityX, velocityY);
    }

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

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() > 0) {
            mHeader = getChildAt(0);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d(getClass().toString(), "onSizeChanged: mHeaderHeight = " + mHeaderHeight);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHeader.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        mHeaderHeight = 0;
        if (mHeader.getLayoutParams() instanceof LinearLayout.LayoutParams) {
            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mHeader.getLayoutParams();
            mHeaderHeight = mHeaderHeight + layoutParams.topMargin + layoutParams.bottomMargin;
        }
        mHeaderHeight = mHeaderHeight + mHeader.getMeasuredHeight();
        int totalHeight;
        if (mIsNeedNestedScroll) {
            totalHeight = MeasureSpec.getSize(heightMeasureSpec) + mHeader.getMeasuredHeight();
        } else {
            totalHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(totalHeight, mode));
    }

    public boolean isIsNeedNestedScroll() {
        return mIsNeedNestedScroll;
    }

    public void setIsNeedNestedScroll(boolean mIsNeedNestedScroll) {
        this.mIsNeedNestedScroll = mIsNeedNestedScroll;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值