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