一个可拉伸的的ScrollView

本文介绍了一种自定义的DraggingView视图,该视图实现了拖拽滚动功能,包括顶部下拉和底部上拉的阻尼效果,以及触摸事件的拦截和处理。DraggingView通过重写onMeasure、onInterceptTouchEvent和onTouchEvent方法,实现了视图的测量、触摸事件的判断和滚动操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂时没有加入RecyclerView中具备的惯性滑动

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.Scroller;

public class DraggingView extends FrameLayout {
    private static final String TAG = "DraggingView";

    private Scroller scroller;

    private View contentView;
    private int maxScrollHeight;

    public DraggingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.scroller = new Scroller(context);

        //minSlideDistance = ViewConfiguration.get(context).getScaledTouchSlop();
        minSlideDistance = 5;
    }

    public boolean childViewReachTop() {
        if (contentView == null) {
            contentView = getChildAt(0);
        }
        if (contentView == null) {
            Log.e(TAG, "DraggingView has no child view");
            return false;
        }
        contentView.measure(0, 0);
        maxScrollHeight = contentView.getMeasuredHeight() - getMeasuredHeight();
        return getScrollY() <= 0;
    }

    public boolean childViewReachBottom() {
        if (contentView == null) {
            contentView = getChildAt(0);
        }
        if (contentView == null) {
            Log.e(TAG, "DraggingView has no child view");
            return false;
        }
        contentView.measure(0, 0);
        maxScrollHeight = contentView.getMeasuredHeight() - getMeasuredHeight();
        return getScrollY() >= maxScrollHeight;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (contentView == null) {
            contentView = getChildAt(0);
        }
        if (contentView == null) {
            Log.e(TAG, "DraggingView has no child view");
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        contentView.measure(widthMeasureSpec, 0);
        LayoutParams layoutParams = (LayoutParams) contentView.getLayoutParams();
        layoutParams.height = contentView.getMeasuredHeight();
        //layoutParams.width = contentView.getMeasuredWidth();
        contentView.setLayoutParams(layoutParams);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private int minSlideDistance;
    private int currY;
    private int prevY;

    private static final int INVALID_POINTER = -1;
    private int pointerIndex = INVALID_POINTER;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                currY = (int) ev.getY();
                prevY = (int) ev.getY();

                pointerIndex = ev.getPointerId(0);
                break;
            case MotionEvent.ACTION_MOVE:
                currY = (int) ev.getY();
                int scrolledY = prevY - currY;
                if (Math.abs(scrolledY) >= minSlideDistance) {
                    return true;
                }
                prevY = currY;
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int actionIndex = ev.getActionIndex();

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                currY = (int) ev.getY();
                prevY = (int) ev.getY();

                pointerIndex = ev.getPointerId(0);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                pointerIndex = ev.getPointerId(actionIndex);
                prevY = (int) (ev.getY(actionIndex));
                break;
            case MotionEvent.ACTION_MOVE:
                final int index = ev.findPointerIndex(pointerIndex);
                if (index < 0) {
                    Log.e(TAG, "pointer index for id " + pointerIndex + " not found.");
                    return false;
                }
                currY = (int) ev.getY(index);
                int scrolledY = prevY - currY;
                //pull down at top
                if (childViewReachTop() && scrolledY <= 0) {
                    double dampingCoefficient = Math.pow(Math.abs(getScrollY()), 1.0 / 4.7);
                    if (dampingCoefficient == 0) {
                        dampingCoefficient = 1;
                    }
                    this.scrollBy(0, (int) (scrolledY / dampingCoefficient));
                }
                //pull up at bottom
                else if (childViewReachBottom() && scrolledY > 0) {
                    double dampingCoefficient = Math.pow(Math.abs(getScrollY() - maxScrollHeight), 1.0 / 4.7);
                    if (dampingCoefficient == 0) {
                        dampingCoefficient = 1;
                    }
                    this.scrollBy(0, (int) (scrolledY / dampingCoefficient));
                }
                //normal drag
                else {
                    this.scrollBy(0, scrolledY);
                }
                prevY = currY;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                if (ev.getPointerId(actionIndex) == pointerIndex) {
                    final int newIndex = actionIndex == 0 ? 1 : 0;
                    pointerIndex = ev.getPointerId(newIndex);
                    prevY = (int) ev.getY(newIndex);
                }
                break;
            case MotionEvent.ACTION_UP:
                //top rebound
                if (childViewReachTop()) {
                    startScroll(0, getScrollY(), 0, -getScrollY(), 310);
                }
                //bottom rebound
                else if (childViewReachBottom()) {
                    startScroll(0, getScrollY(), 0, -getScrollY() + maxScrollHeight, 310);
                }
                //no rebound
                else {
                    //startScroll(0, getScrollY(), 0, -getScrollY(), 310);
                }
                break;
        }
        return true;
    }

    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        scroller.startScroll(startX, startY, dx, dy, duration);
        postInvalidate();
    }

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

}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值