共计 5106 个字符,预计需要花费 13 分钟才能阅读完成。
零、前言
自定义 View 经常和事件打交道,不过那个 event 对象用着感觉挺麻烦
打算自己写一个事件的解析类来辅助事件的分析,功能包括:
1. 点击监听:回调 --> 传出落点 (类型 PointF)
2. 抬起监听:回调 --> 手指抬起点 (类型 PointF)、移动方向 (类型 Orientation, 八个)
3. 移动监听:回调 --> 速度 (double) y 位移 (float) x 位移 (float) 角度 (double)、移动方向
4. 是否移动、是否按下的判断 -------- 源码比较简单,我注释也很清楚,都贴在文尾,自行 cv。
测试效果:
1. 方向测试
2. 角度、位移测试
3. 速度解析
一、使用:
1.view 初始化时初始化 EventParser 并为 EventParser 设置监听器
2. 在 onTouchEvent 里为 mEventParser 设置解析对象 (不止是 view,在 Activity 中也可以用, 只有有 event)
public class EventView extends View {
private EventParser mEventParser;
public EventView(Context context) {this(context, null);
}
public EventView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);
init();}
private void init() {mEventParser = new EventParser();// 初始化 EventParser
// 为 EventParser 设置监听器
mEventParser.setOnEventListener(new OnEventListener() {
@Override
public void down(PointF pointF) { }
@Override
public void up(PointF pointF, Orientation orientation) { }
@Override
public void move(double v, float dy, float dx, double dir, Orientation orientation) {}});
}
@Override
public boolean onTouchEvent(MotionEvent event) {mEventParser.parseEvent(event);// 设置解析对象
return true;
}
}
这样所有的这些事件参数就是你的了。
当然也提供了适配器,只想用一个回调方法的, 不需要直接实现接口, 你可以:
mEventParser.setOnEventListener(new OnEventAdapter(){
@Override
public void move(double v, float dy, float dx, double dir, Orientation orientation) {}});
二、代码实现
1. 解析器主类:EventParser
/**
* 作者:张风捷特烈 <br/>
* 时间:2018/11/6 0006:20:22<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件解析器
*/
public class EventParser {
private OnEventListener onEventListener;
private Orientation mOrientation = Orientation.NO;
private PointF mTagPos;// 按下坐标点
// 移动时坐标点 --- 在此创建对象,避免在 move 中创建大量对象
private PointF mMovingPos = new PointF(0, 0);
private float detaY = 0;// 下移总量
private float detaX = 0;// 右移总量
private boolean isDown = false;// 是否按下
private boolean isMove = false;// 是否移动
private PointF mDownPos;// 记录按下时点
private long lastTimestamp = 0L;// 最后一次的时间戳
public void setOnEventListener(OnEventListener onEventListener) {this.onEventListener = onEventListener;}
/**
* 添加自己的事件解析
*
* @param event 事件
*/
public void parseEvent(MotionEvent event) {switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDown = true;
// 按下 --- 为 p0 赋值
mTagPos = new PointF(event.getX(), event.getY());
mDownPos = mTagPos;
lastTimestamp = System.currentTimeMillis();
if (onEventListener != null) {onEventListener.down(mTagPos);
}
break;
case MotionEvent.ACTION_MOVE:
// 移动的那刻的坐标 (移动中,不断更新)
mMovingPos.x = event.getX();
mMovingPos.y = event.getY();
// 处理速度
detaX = mMovingPos.x - mDownPos.x;
detaY = mMovingPos.y - mDownPos.y;
// 下移单量
float dx = mMovingPos.x - mTagPos.x;
// 右移单量
float dy = mMovingPos.y - mTagPos.y;
double ds = Math.sqrt(dx * dx + dy * dy);// 偏移位移单量
double dir = deg((float) Math.acos(detaX / ds));// 角度
long curTimestamp = System.currentTimeMillis();
long dt = curTimestamp - lastTimestamp;
// 由于速度是 C *px/ms
double v = ds / dt * 100;
orientationHandler(dir);// 处理方向
if (onEventListener != null) {onEventListener.move(v, detaY, detaX, detaY < 0 ? dir : -dir, mOrientation);
}
if (Math.abs(detaY) > 50 / 3.0) {isMove = true;}
mTagPos.x = mMovingPos.x;// 更新位置
mTagPos.y = mMovingPos.y;// 更新位置 ---- 注意这里不能让两个对象相等
lastTimestamp = curTimestamp;// 更新时间
break;
case MotionEvent.ACTION_UP:
if (onEventListener != null) {onEventListener.up(mTagPos, mOrientation);
}
reset();// 重置工作
break;
}
}
/**
* 重置工作
*/
private void reset() {
isDown = false;// 重置按下状态
isMove = false;// 重置移动状态
mDownPos.x = 0;// 重置:mDownPos
mDownPos.y = 0;// 重置:mDownPos
mOrientation = Orientation.NO;// 重置方向
}
/**
* 处理方向
*
* @param dir 方向
*/
private void orientationHandler(double dir) {if (detaY < 0 && dir > 70 && dir < 110) {mOrientation = Orientation.TOP;}
if (detaY > 0 && dir > 70 && dir < 110) {mOrientation = Orientation.BOTTOM;}
if (detaX > 0 && dir < 20) {mOrientation = Orientation.RIGHT;}
if (detaX < 0 && dir > 160) {mOrientation = Orientation.LEFT;}
if (detaY < 0 && dir <= 70 && dir >= 20) {mOrientation = Orientation.RIGHT_TOP;}
if (detaY < 0 && dir >= 110 && dir <= 160) {mOrientation = Orientation.LEFT_TOP;}
if (detaX > 0 && detaY > 0 && dir >= 20 && dir <= 70) {mOrientation = Orientation.RIGHT_BOTTOM;}
if (detaX < 0 && detaY > 0 && dir >= 110 && dir <= 160) {mOrientation = Orientation.LEFT_BOTTOM;}
}
public boolean isDown() {return isDown;}
public boolean isMove() {return isMove;}
/**
* 弧度制化为角度制
*
* @param rad 弧度
* @return 角度
*/
private float deg(float rad) {return (float) (rad * 180 / Math.PI);
}
}
2. 方向枚举:
/**
* 作者:张风捷特烈 <br/>
* 时间:2018/11/15 0015:8:14<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:移动方向枚举
*/
public enum Orientation {NO("无"),// 无
TOP("上"), // 上
BOTTOM("下"),// 下
LEFT("左"),// 左
RIGHT("右"),// 右
LEFT_TOP("左上"),// 左上
RIGHT_TOP("右上"), // 右上
LEFT_BOTTOM("左下"),// 左下
RIGHT_BOTTOM("右下")// 右下
private String or;
Orientation(String or) {this.or = or;}
public String value() {return or;}
}
3. 事件监听回调
/**
* 作者:张风捷特烈 <br/>
* 时间:2018/11/15 0015:8:13<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件监听回调
*/
public interface OnEventListener {
/**
* 点击
*
* @param pointF 落点
*/
void down(PointF pointF);
/**
* 抬起
*
* @param pointF 抬起点
* @param orientation 方向
*/
void up(PointF pointF, Orientation orientation);
/**
* 移动
*
* @param v 速度
* @param dy y 位移
* @param dx x 位移
* @param dir 角度
* @param orientation 方向
*/
void move(double v, float dy, float dx, double dir, Orientation orientation);
}
4. 事件处理适配器
/**
* 作者:张风捷特烈 <br/>
* 时间:2018/11/15 0015:8:18<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件处理适配器
*/
public class OnEventAdapter implements OnEventListener {
@Override
public void down(PointF pointF) { }
@Override
public void up(PointF pointF, Orientation orientation) { }
@Override
public void move(double v, float dy, float dx, double dir, Orientation orientation) {}}
后记:捷文规范
1. 本文成长记录及勘误表
项目源码 | 日期 | 备注 |
---|---|---|
V0.1– 无 | 2018-11-15 | Android 自定义控件辅助利器之 EventParser |
2. 声明
1—- 本文由张风捷特烈原创, 转载请注明
2—- 欢迎广大编程爱好者共同交流, 微信:zdl1994328
3—- 个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4—- 看到这里,我在此感谢你的喜欢与支持
正文完