双层布局 DoubleLayerLayout (续)

前言

最近看博客,发现自己的实现复杂了,所以优化了一下。之前之所以不断的layout,是因为scroll没起效果。最近才明白,scroll一个视图的时候,内容是移动的,而background是不移动的。所以在这里,fgView的background应该为null。此外,学习使用Scroller类。

  • Scroller类的构造器可以传入一个补差器,让滑动更自然。
  • 调用startScroll函数之后,需要调用invalidate(),invalidate()会触发computeScroll()。

此外,与我原以为的相反,视图向下滑动的时候,scrollY为负值,因为srcollY代表的是显示窗口相对于视图顶部的偏移。我们知道,ScrollView 的内容长度是比窗口长度大的,所以会有这么一个偏移值。

源码

public class DoubleLayerRelativeLayout extends RelativeLayout {

    private static final int SNAP_VELOCITY_THRESHOLD = 600;
    private static final int DURATION = 200;

    private View bgView;
    private View fgView;

    private Scroller scroller;
    private VelocityTracker velocityTracker;

    private float lastY;
    private int originalTop;

    /**
     * if totalDy > dyThreshold, show {@link #bgView}
     */
    private float dyThreshold;

    public DoubleLayerRelativeLayout(Context context) {
        super(context);
        init();
    }

    public DoubleLayerRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DoubleLayerRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        scroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        bgView = getChildAt(0);
        fgView = getChildAt(1);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        originalTop = getTop();

        dyThreshold = getHeight() * 0.25f;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (super.onTouchEvent(event)) {
            return true;
        }
        addVelocityTracker(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                handleDownEvent(event);
                break;
            case MotionEvent.ACTION_MOVE:
                handleMoveEvent(event);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                handleUpEvent(event);
                break;
        }
        return true;
    }

    private void handleDownEvent(MotionEvent ev) {
        lastY = ev.getY();
    }

    private void handleMoveEvent(MotionEvent ev) {
        // update the position of fgView
        float currY = ev.getY();
        float dy = currY - lastY;
        lastY = currY;

        fgView.scrollBy(0, (int) -dy);
    }

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

    private void handleUpEvent(MotionEvent ev) {
        int curScrollY = fgView.getScrollY();
        float snapVelocity = getYVelocity();
        if ((curScrollY < dyThreshold && snapVelocity < SNAP_VELOCITY_THRESHOLD) || snapVelocity < -SNAP_VELOCITY_THRESHOLD) {
            // reset
            scroller.startScroll(0, curScrollY, 0, -curScrollY);
        } else {
            // show bgView
            scroller.startScroll(0, curScrollY, 0, (int) (-getMeasuredHeight()*0.5F-curScrollY));
        }
        invalidate();

        recycleVelocityTracker();
    }

    private void addVelocityTracker(MotionEvent event) {
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
    }

    private void recycleVelocityTracker() {
        if (velocityTracker != null) {
            velocityTracker.recycle();
            velocityTracker = null;
        }
    }

    private int getYVelocity() {
        velocityTracker.computeCurrentVelocity(1000);
        int velocity = (int) velocityTracker.getYVelocity();
        return velocity;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值