自定义可拖拽view

本文介绍了自定义View的拖动功能实现,通过重写Layout过程来重新定位View。关键在于理解getRawX()与getX()的区别:getRawX()返回触摸点相对于屏幕的坐标,而getX()返回相对于View本身的坐标。在拖动过程中,由于布局变化,使用getX()计算偏移量时,不需要每次Layout后重新赋值。

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

我们知道View的绘制有三个重要的过程分别是Measure,Layout,Draw.Measure负责测量View的尺寸,Layout负责定位View的位置,Draw负责把View绘制到图片上。我们这就是通过Layout重新定位View。

public class CustomMoveView extends View {
    private float lastX = 0, lastY = 0;//记录上一次点击  的点的坐标
    private int widthPixels, heightPixels;//手机屏幕的宽、高,用于处理越界

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

    public CustomMoveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics dm2 = getResources().getDisplayMetrics();
        widthPixels = dm2.widthPixels;//屏幕宽度
        heightPixels = dm2.heightPixels;//屏幕高度

    }

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

    @Override
    public void computeScroll() {
        super.computeScroll();
    }

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

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                /**
                 * 记录第一次点击点的坐标
                 * getRawX()、getRawY()触摸点相对屏幕左上角 的位置坐标
                 *
                 **/
                lastX = event.getRawX();
                lastY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:

                int offsetX = (int) (event.getRawX() - lastX);//X轴方向上的偏移量
                int offsetY = (int) (event.getRawY() - lastY);//Y轴方向上的偏移量
                /**
                 *way 1 :
                 *layout(r,t,l,b) 四个参数 左上角 和右下角的坐标
                 * getLeft()获取此view左边缘到父view的距离
                 * getTop()获取此view上边缘到父view的距离
                 * getRight()获取此view右边缘到父view的距离
                 * getBotton()获取此view下边缘到父view的距离
                 **/
                layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);

                /**
                 * 距离当前点的位置,便于计算下次偏移量
                 * **/
                lastX = event.getRawX();
                lastY = event.getRawY();


                /**
                 way 2 :
                 **/
//                offsetLeftAndRight(offsetX);
//                offsetTopAndBottom(offsetY);


                break;
            case MotionEvent.ACTION_UP:

                int owenW = getWidth();//自身的长度
                int owenY = getHeight();//自身的高度
                int left = getLeft(), top = getTop(), rignt = getRight(), bottom = getBottom();
//
//                判断是否越界,如果移出屏幕,则抬起时让View自动回到屏幕内
                if (getLeft() < 0) {
                    left = 0;
                    rignt = owenY;
                }

                if (getTop() < 0) {
                    top = 0;
                    bottom = owenY;

                }

                if (getRight() > widthPixels) {
                    left = widthPixels - owenW;
                    rignt = widthPixels;
                }

                if (getBottom() > heightPixels) {
                    top = heightPixels - owenY;
                    bottom = heightPixels;
                }


                layout(left, top, rignt, bottom);


                break;


        }

        return true;
    }


}

代吗很简单,相关方法都有注释。来看下用法,像TextView一样在xml使用一样
<com.jsh.frame.myframe2.wightview.CustomMoveView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="34dp"
android:layout_marginTop="90dp"
android:background="@color/red_500"
/>

这里想说一下getX()与getRawX()的区别。
MotionEvent下面有两个方法:
getRawX(); //触摸点相对于屏幕的x坐标。
getX(); //你第一个手指触摸点相对于触摸的控件本身的x坐标。如果你两个手指下去,想获取第二的触摸位置就调用get(1),后面依次类推。
上面onTouchEvent中用getRawX()替换成getX(),代码如下:
`
public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            /**
             * 记录第一次点击点的坐标
             **/
            lastX = event.getX();
            lastY = event.getY();
            break;
        case MotionEvent.ACTION_MOVE:

            int offsetX = (int) (event.getX() - lastX);//X轴方向上的偏移量
            int offsetY = (int) (event.getY() - lastY);//Y轴方向上的偏移量

            layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);

            break;
        case MotionEvent.ACTION_UP:

            int owenW = getWidth();//自身的长度
            int owenY = getHeight();//自身的高度
            int left = getLeft(), top = getTop(), rignt = getRight(), bottom = getBottom();

       /**判断是否越界**/


            if (getLeft() < 0) {
                left = 0;
                rignt = owenY;
            }

            if (getTop() < 0) {
                top = 0;
                bottom = owenY;

            }

            if (getRight() > widthPixels) {
                left = widthPixels - owenW;
                rignt = widthPixels;
            }

            if (getBottom() > heightPixels) {
                top = heightPixels - owenY;
                bottom = heightPixels;
            }


            layout(left, top, rignt, bottom);


            break;


    }

    return true;
}

`
与getRawX不同就是 每次Layout 后不用重新赋值 lastX、lastY.这是因为getRawX计算的偏移量是相对屏幕的,getX计算的偏移量是相对view本身的。比容下图中 view水平向右移动两次,触摸点从A移动到B再移动到C。A—>B : getRawX计算的偏移量是getRawX(b)-getRawX(a);B—>C : getRawX计算的偏移量是getRawX(c)-getRawX(b)所以每次layout后lastX要重新赋值,这很好理解。getX 是获取相对控件本身坐标值,B相对屏幕的坐标是(50,10),Layout完成后,B相对View的坐标就变成了(10,10),
C相对屏幕的坐标是(90,10),Layout完成后,C相对View的坐标也变回了(10,10),当Layout完成后,上一次的参考点坐标自己已经改变了,也就不用我们再去给他赋值了(我第一次看这里有点犯模糊,这里记录一下我自己的理解过程,脑子灵活的同学可以绕过)
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值