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);
}
};