Android自定义控件1EventParser

47次阅读

共计 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—- 看到这里,我在此感谢你的喜欢与支持

正文完
 0