自定义回弹的ScrollView

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_36533134/article/details/85236948

有效果图才有看下去的动力,也能快速的知道是不是你想要的

                                                                        


一、实现思路

ScrollView中只能有个子View。我们的目的就是在这个子View到达顶部或是底部的时,当我们继续向下或向上滑动的时候依然可以让我们的子View向下或是向上移动,当我们松开的时候回到顶部或是底部。

二、具体分析

1、需要判断View是否到顶部或是底部

    getScrollY() == 0;                                           // 到顶(有坑:必须知道子View的大小不然一直都是0)       
    getScrollY() == childView.getMeasuredHeight() - getHeight(); // 到底

2、在开始移动子View的时候,要保存初始状态,目的是为了之后的恢复初始状态。这里我们用一个Rect 做参照物在onLayout中初始化大小(位置),用的参数是子View的属性

     rect.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());

3、在onTouchEvent中的 MotionEvent.ACTION_MOVE 中从新放置我们的子View,先判断判断是否可以开始移动,然后开始移动,(如果在down记录按下位置,记录移动距离的话,或出现手指移动距离反应在视图上等指数增长,因为childView.getTop = childView.getTop + distanceY,distanceY在增大childView.getTop也在增大,所以就是用了一个相对距离)


                if (isNeedMove()) {
                    float startY = y;// 上次位置就是现在开始的位置
                    float nowY = ev.getY();
                    int distanceY = (int) (nowY - startY);// 现在的位置和上次位置的差
                    if (!isCount) {// 保证每次从0开始,因为这里的startY是全局变量 有点时候第一次 nowY - startY<0 有的时候>>0
                        distanceY = 0; // 在这里要归0.
                    }
                    y = nowY;// 记录上一次位置
                    // 从新摆放子View的位置
                    Log.d("aaa", "distanceY = " + distanceY + "---childView.getTop()=" + childView.getTop());
                    childView.layout(
                            childView.getLeft(),
                            childView.getTop() + distanceY / 3,
                            childView.getRight(),
                            childView.getBottom() + distanceY / 3);

                    isCount = true;
                }

4、 在onTouchEvent中的 MotionEvent.ACTION_UP 做回弹操作就是就是将我们的子View放在最开始的位置,还记得我们定义的参考物吗?这个时候他就起到了作用,将参照物的位置属性赋值给我们的子View,让我们的子View回到正常的位置,加上动画会自然一些

    /**
     * 重置子View
     */
    private void resetChildView() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, childView.getTop(), rect.top);
        animation.setDuration(200);
        childView.startAnimation(animation);
        childView.layout(rect.left, rect.top, rect.right, rect.bottom);
        isCount = false;
    }

三、完整代码 

public class SpringScrollView extends ScrollView {

    private View childView;// 子view
    private Rect rect = new Rect();// 复位用的参照物
    private float downY;// 记录上一个位置
    private float y;// 记录上一个位置
    private boolean isCount;// 是否重置距离
    private Paint paint;
    private Rect tvRect = new Rect();
    private final String content;

    public SpringScrollView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setTextSize(50);
        paint.setColor(Color.GRAY);
        content = "我是一个回弹的ScrollView";
    }

    /**
     * 布局填充完毕,获取子View对象
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() > 0) {
            childView = getChildAt(0);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        // 设置复位参照
        rect.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 画文字
        paint.getTextBounds(content, 0, content.length() - 1, tvRect);
        canvas.drawText(content, (getWidth() - tvRect.width()) / 2, tvRect.height() * 1.2f, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (isNeedMove()) {
                    float startY = y;// 上次位置就是现在开始的位置
                    float nowY = ev.getY();
                    int distanceY = (int) (nowY - startY);// 现在的位置和上次位置的差
                    if (!isCount) {// 保证每次从0开始,因为这里的startY是全局变量 有点时候第一次 nowY - startY<0 有的时候>>0
                        distanceY = 0; // 在这里要归0.
                    }
                    y = nowY;// 记录上一次位置
                    // 从新摆放子View的位置
                    Log.d("aaa", "distanceY = " + distanceY + "---childView.getTop()=" + childView.getTop());
                    childView.layout(
                            childView.getLeft(),
                            childView.getTop() + distanceY / 3,
                            childView.getRight(),
                            childView.getBottom() + distanceY / 3);
                    isCount = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                // 重置子View
                if (isCount) {
                    resetChildView();
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 重置子View
     */
    private void resetChildView() {
        TranslateAnimation animation = new TranslateAnimation(0, 0, childView.getTop(), rect.top);
        animation.setDuration(200);
        childView.startAnimation(animation);
        childView.layout(rect.left, rect.top, rect.right, rect.bottom);
        isCount = false;
    }

    /**
     * 判断是否需要弹性
     *
     * @return
     */
    public boolean isNeedMove() {
        int offset = childView.getMeasuredHeight() - getHeight();// 到底
        int scroll = getScrollY();// 到顶
        return scroll == 0 || offset == scroll;
    }
}

最后有一个小问题,我发现网上但部分弹性ScrollView都会有着问题,在我下拉回弹不松手,然后向上移动的时候出现的,感觉ScrollView也跟着向上走了,应该因为这个时候,子view.getTop>0但是却不走重置子view的方法导致。希望哪位大能可以帮个忙,解决一下。如果哪天我知道了 我也会解决更新一下。

                                                       

猜你喜欢

转载自blog.csdn.net/sinat_36533134/article/details/85236948
今日推荐