Android 页面中悬浮拖拽控件源码阅读

GitHub地址
随手指移动的控件, 松开手指自动滑动到屏幕侧边

onTouchEvent()主要流程
//该View拖拽开始的坐标
private int lastX;
private int lastY;
@Override
public boolean onTouchEvent(MotionEvent event) {
    // 获取手指相对于屏幕左上角坐标
    int rawX = (int) event.getRawX();
    int rawY = (int) event.getRawY();
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            ...
            // 手指第一次按下的坐标由lastX,lastY保存.
            lastX = rawX;
            lastY = rawY;
            break;
        case MotionEvent.ACTION_MOVE:
            ...
            // 计算手指滑动距离
            int dx = rawX - lastX;
            int dy = rawY - lastY;
            // this.getX():获取DragFloatingActionButton控件左边距离父控件左边的距离
            // 所以,x代表手指移动过后DragFloatingActionButton控件左边距离父控件左边的距离.
            float x = this.getX() + dx;
            // 同理
            float y = this.getY() + dy;
			...
            // 设置DragFloatingActionButton控件左边距离父容器左边的距离
            setX(x);
            // 同理
            setY(y);
            // 更新手指按下的起始坐标值,为下一次滑动做准备.
            lastX = rawX;
            lastY = rawY;
            break;
        case MotionEvent.ACTION_UP:
            ...
            break;
    }
    return ...;
}
onTouchEvent()完整代码
@Override
public boolean onTouchEvent(MotionEvent event) {
     // 获取手指相对于屏幕左上角坐标
    int rawX = (int) event.getRawX();
    int rawY = (int) event.getRawY();
    //event.getAction() & MotionEvent.ACTION_MASK 解释:http://blog.sina.com.cn/s/blog_82f01d350101gbbf.html
    //不管多少根手指操作, 最终得到的还是Down,Up,Move 事件.
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            this.setAlpha(0.9f);
            setPressed(true);
            isDrag = false;
            //请求父容器不要拦截Down事件
            getParent().requestDisallowInterceptTouchEvent(true);
            // 手指第一次按下的坐标由lastX,lastY保存.
            lastX = rawX;
            lastY = rawY;
            if (getParent() != null) {
                parent = (ViewGroup) getParent();
                parentHeight = parent.getHeight();
                parentWidth = parent.getWidth();
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (parentHeight <= 0.2 || parentWidth <= 0.2) {
                isDrag = false;
                break;
            } else {
                isDrag = true;
            }
            this.setAlpha(0.9f);
            // 计算手指滑动距离
            int dx = rawX - lastX;
            int dy = rawY - lastY;
            //这里修复一些华为手机无法触发点击事件
            int distance = (int) Math.sqrt(dx * dx + dy * dy);
            if (distance == 0) {
                isDrag = false;
                break;
            }
            // this.getX():获取DragFloatingActionButton控件左边距离父控件左边的距离
            // 所以,x代表手指移动过后DragFloatingActionButton控件左边距离父控件左边的距离.
            float x = this.getX() + dx;
            // 同理
            float y = this.getY() + dy;
            //如果拖拽值为X/Y轴负方向, 则拖拽距离按0px计算
            //x > parentWidth - getWidth() 表示拖拽距离如果大于当前View可拖拽的距离,则按照可以拖拽的最大距离计算.(父控件宽度减去当前View宽度)
            x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
            y = y < 0 ? 0 : y > parentHeight - getHeight() ? parentHeight - getHeight() : y;
            // 设置DragFloatingActionButton控件左边距离父容器左边的距离
            setX(x);
            setY(y);
           // 更新手指按下的起始坐标值,为下一次滑动做准备.
            lastX = rawX;
            lastY = rawY;
            break;
        case MotionEvent.ACTION_UP:
            if (!isNotDrag()) {
                // 确认在滑动
                // 设置为非按压状态
                setPressed(false);
                // 一旦松开,就自动靠边
                moveHide(rawX);
            } else {
                //没有滑动就设置透明度
                myRunable();
            }
            break;
    }
    //如果是拖拽则消耗事件,否则正常传递即可。
    return !isNotDrag() || super.onTouchEvent(event);
}
isNotDrag()
// 该方法主要用来判断该控件是否处于滑动
// isDrag = false 没有滑动
// isDrag = true 正在滑动
private boolean isNotDrag() {
    // isDrag ==  false,且DragFloatingActionButton左边距离父控件左边距离为0或者DragFloatingActionButton左边距离父控件右边距离为0,
    // 满足以上三个条件,则DragFloatingActionButton没有滑动.
    return !isDrag && (getX() == 0 || (getX() == parentWidth - getWidth()));
}
moveHide()
//手指松开, 该View自动移动到父控件侧边
private void moveHide(int rawX) {
    if (rawX >= parentWidth / 2) {
        //靠右吸附
        animate().setInterpolator(new DecelerateInterpolator())
                .setDuration(500)
                .xBy(parentWidth - getWidth() - this.getX())//parentWidth - getWidth() - getX() 等于控件距离父控件侧边的距离
                .start();
        // 设置透明度
        myRunable();
    } else {
        //靠左吸附
        ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);
        oa.setInterpolator(new DecelerateInterpolator());
        oa.setDuration(500);
        oa.start();
        // 设置透明度
        myRunable();
    }
}
// 设置透明度,延迟2s
private void myRunable() {
    handler.removeCallbacks(runnable);
    handler.postDelayed(runnable, 2000);
}
// 执行Runnable.run()来为控件设置透明度
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        setAlpha(0.3f);
    }
};
示例

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值