Android自定义控件辅助利器之EventParser

时间:2022-06-15
本文章向大家介绍Android自定义控件辅助利器之EventParser,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

零、前言

自定义View经常和事件打交道,不过那个event对象用着感觉挺麻烦 打算自己写一个事件的解析类来辅助事件的分析,功能包括: 1.点击监听:回调-->传出落点(类型PointF) 2.抬起监听:回调-->手指抬起点(类型PointF)、移动方向(类型Orientation,八个) 3.移动监听:回调-->速度(double) y位移(float) x位移(float) 角度(double)、移动方向 4.是否移动、是否按下的判断--------源码比较简单,我注释也很清楚,都贴在文尾,自行cv。

测试效果:

1.方向测试

方向测试.gif

2.角度、位移测试

角度、位移测试.gif

3.速度解析

速度解析.gif

一、使用:

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) {

    }
}