目录
前言
一、继承系统控件
二、重写方法onTouchEvent()
- 1.变量
- 2.具体重写方法代码
- 3.边界限制(在移动时我们要防止超出边界)
- 3.不足:该自定义View暂时不能用在最外层父布局为NestedScrollView的布局里(暂时在排查原因中)
总结
前言
随着安卓技术的接触和深入,产品需求也越来越离谱,一个个优美的UI和高大上的功能,仅仅使用系统提供的控件是无法完成的,而自定义View就能够满足产品需求。近日,我便做到了一个需求,需要一个可以拖拽的悬浮按钮,那就拿出我三脚猫功夫的自定义View能力了。
无图无据:
一、继承系统控件
系统已经有了现成的悬浮按钮:ExtendedFloatingActionButton和FloatingActionButton,那么我们便图简单直接继承这两个控件就行。
因为我要做的是一个带文字的悬浮按钮,所以我选择继承ExtendedFloatingActionButton。如下:
public class DraggableFab extends ExtendedFloatingActionButton {}
二、重写方法onTouchEvent()
1.变量
private int lastAction = MotionEvent.ACTION_DOWN; // 上次的点击类型
private float startX; // 开始的位置X
private float startY; // 开始的位置Y
private float lastX; // 最开始的位置X
private float lastY; // 最开始的位置Y
private int parentWidth;// 父布局的宽
private int parentHeight; // 父布局的高
2.具体重写方法代码
- 首先在点击按钮时候记录当前初始位置
- 在移动按钮时重新设置当前View的位置
- 在松开按钮时判断是否移动,决定是否触发点击事件 代码如下(示例):
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
// 可以点击
setPressed(true);
switch (action) {
case MotionEvent.ACTION_DOWN:
getParentDimensions();
lastAction = MotionEvent.ACTION_DOWN;
startX = getX() - event.getRawX();
startY = getY() - event.getRawY();
lastX = getX();
lastY = getY();
break;
case MotionEvent.ACTION_MOVE:
lastAction = MotionEvent.ACTION_MOVE;
setX(event.getRawX() + startX);// 设置位置
setY(event.getRawY() + startY);
checkBoundaries();
// 返回true消费该事件
break;
case MotionEvent.ACTION_UP:
// 没有移动
if (lastAction == MotionEvent.ACTION_DOWN) {
// 移动像素小于10
} else if (Math.abs(getX() - lastX) < 10 && Math.abs(getY() - lastY) < 10) {
}else {
// 变成点击
lastAction = MotionEvent.ACTION_DOWN;
// 设置不可点击
setPressed(false);
}
break;
default:
return false;
}
// 保留原有的水波纹效果
return super.onTouchEvent(event);
}
3.边界限制(在移动时我们要防止超出边界)
- 这里我们就要先获取父布局的宽高
- 在根据当前View的位置来判断是否越界
- 越界者则重新设置View在边界,不越界则不做处理
/**
* 获取父布局宽高
*/
private void getParentDimensions() {
if (getParent() != null && getParent() instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) getParent();
parentWidth = parent.getWidth();
parentHeight = parent.getHeight();
}
}
/**
* 边界限制
*/
private void checkBoundaries() {
// 左边
if (getX() < 0) {
setX(0);
}
if (getY() < 0) {
setY(0);
}
// 这里可以根据所需隐藏部分View在屏幕外,我这里使用了工具类,将17转化为17sp,有兴趣的同学可以在评论区留言要,当然如果不需要隐藏View,只需要改为注释代码即可
if (getX() > parentWidth - getWidth()+ DensityUtil.dip2px(getContext(),17)) {
setX(parentWidth - getWidth()+ DensityUtil.dip2px(getContext(),17));
}
if (getY() > parentHeight - getHeight()) {
setY(parentHeight - getHeight());
}
// if (getX() > parentWidth - getWidth()) {
// setX(parentWidth - getWidth());
// }
// if (getY() > parentHeight - getHeight()) {
// setY(parentHeight - getHeight());
// }
}
3.不足:该自定义View暂时不能用在最外层父布局为NestedScrollView的布局里(暂时在排查原因中)
总结
虽然这是一个小小功能,但足见自定义View技术在安卓里的重要性,我会在完善这个自定义View的基础下接着深入学习相关知识,希望大家一起加油。