Android自定义控件:自定义拖动控件(上)

最近在写一个拖动控件的功能的时候,开始从实现控件移动方法入手,发现重复使用的时候非常麻烦。干脆继承原件重写一个可以拖动的控件,此日志为过程记录。(的代码框太晃眼,建议夜间模式下浏览)

下面先用直接设置OnTouch监听事件的方法实现:

xml布局



    

要实现拖动功能,必须将clickable属性设为true,这样拖动之前的接触动作才能被捕获,而点击事件不用将clickable属性设为true是因为在点击监听事件中会直接将clickable设置为真。

Java代码

package com.cycycd.testt;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    ImageView he;
    int markX,markY;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        he=(ImageView) findViewById(R.id.moveview);
        he.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int ev=event.getAction();
                switch (ev)
                {
                    //按下时
                    case MotionEvent.ACTION_DOWN:
                        //获取初始位置
                        markX=(int)event.getRawX();
                        markY=(int)event.getRawY();
                        break;
                    //移动时
                    case MotionEvent.ACTION_MOVE:
                        //求偏移量
                        int dx = (int) event.getRawX() - markX;
                        int dy = (int) event.getRawY() - markY;
                        int left = v.getLeft() + dx;
                        int top = v.getTop() + dy;
                        int right = v.getRight() + dx;
                        int bottom = v.getBottom() + dy;
                        //设置位置
                        v.layout(left, top, right, bottom);
                        //重置初始位置
                        markX=(int)event.getRawX();
                        markY=(int)event.getRawY();
                        break;
                }
                return false;
            }
        });
    }
}

在Activity中为绑定ImageView并设置触摸监听事件,这里只写了最简单的拖动,也没写吸附和边界碰撞之类的。因为不会用手机录GIF,所以就不放效果了,诸位一试便知。以上这是最简单的实现方法,如果要实时获取坐标的话,在重置初始位置的两句代码执行后markX和markY即为有效坐标,可以紧跟后面进行进一步的操作。

如果需要多次使用可拖动控件的话,这样写会非常麻烦,而且程序结构会杂乱无章,所以需要将相关的功能封装,也就是自己动手写一个控件。

首先,自定义一个继承自ImageView的MoveImageView类(当然,这里也可以直接继承View,但是少了很多非常方便的方法)继承之后实现父类的构造方法:

public class MoveImageView extends ImageView {
    public MoveImageView(Context context) {
        super(context);
    }

    public MoveImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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

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

接下来也很简单,直接在构造函数中调用setOnTouchListener为控件设置触摸事件,内容和上面直接设置的相同,不过要放在构造函数中,为了方便重复使用直接打包成方法,代码如下:

public void setMove()
{
    this.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            //markX,markY建议在类中声明作为全局变量
            int markX,markY;
            int ev=event.getAction();
            switch (ev)
            {
                //按下时
                case MotionEvent.ACTION_DOWN:
                    //获取初始位置
                    markX=(int)event.getRawX();
                    markY=(int)event.getRawY();
                    break;
                //移动时
                case MotionEvent.ACTION_MOVE:
                    //求偏移量
                    int dx = (int) event.getRawX() - markX;
                    int dy = (int) event.getRawY() - markY;
                    int left = v.getLeft() + dx;
                    int top = v.getTop() + dy;
                    int right = v.getRight() + dx;
                    int bottom = v.getBottom() + dy;
                    //设置位置
                    v.layout(left, top, right, bottom);
                    //重置初始位置
                    markX=(int)event.getRawX();
                    markY=(int)event.getRawY();
                    break;
            }
            return false;
        }
    });
}

将此方法在构造函数中调用,即完成部署。

接下来测试此类,在Activity中除基本的绑定界面之外,不需要任何额外代码:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

在xml中,使用刚写好的控件绘制一个布局:



    

测试一下,控件可正常拖动。

这个自定义控件仅仅实现了最基本的拖动功能,还有接口实现和优化之类的下次有时间再说吧:D

你可能感兴趣的:(Android自定义控件:自定义拖动控件(上))