android自定义View长按和点击事件的实现

期末的课程设计要做一个Android端的扫雷游戏,游戏地图使用自定义View画出的一个个方格实现,点击表示打开方格,长按表示标记方格,于是就有了监听自定义View的长按和点击事件的需求。 虽然系统提供了OnClickListener和OnLongClickListener用来监听长按和点击事件,但是我们需要知道点击和长按的位置,显然这两个接口并不能满足要求,所以我决定使用重写自定义View的onTouchEvent(MotionEvent event)方法实现。或许还有其他更简单的方法实现这个功能,但是目前我的水平就这样了,以后如果碰到好的方法再改吧。

so:本文主要讲述通过重写自定义View的onTouchEvent(MotionEvent event)方法实现对自定义View的长按和点击事件的监听,要求获取用户点击和长按的位置。

首先,当用户的手指点击,移动和松开屏幕时,View都会回调onTouchEvent(MotionEvent event)响应事件,事件的类型封装在event中,通过event.getAction()获取(ps:event中封装了很多事件的类型,但本例中只用了这三种,水平有限,其他的不会,不敢瞎写)。当用户手指点在自定义View上时,事件类型为MotionEvent.ACTION_DOWN,当用户手指在自定义View上移动时,事件类型为MotionEvent.ACTION_MOVE,当用户手指从自定义View拿起时,事件类型为MotionEvent.ACTION_UP。

然后来分析下点击和长按所发生的事件类型。点击事件:发生MotionEvent.ACTION_DOWN事件后,短时间内发生MotionEvent.ACTION_UP;长按事件:发生MotionEvent.ACTION_DOWN事件后,经过一段时间发生MotionEvent.ACTION_UP。如果发生MotionEvent.ACTION_DOWN后,发生了MotionEvent.ACTION_MOVE,代表用户既没有点击,也没有长按(即不能发生事件处理)。所以,我们要做的就是在onTouchEvent(MotionEvent event)中判断用户操作,发生MotionEvent.ACTION_DOWN开始计时,如果计时结束前发生了MotionEvent.ACTION_UP,代表用户点击了View,处理点击事件;如果计时结束结束仍没发生MotionEvent.ACTION_UP,代表用户长按了View,处理长按事件;如果计时i期间发生了MotionEvent.ACTION_MOVE,代表用户取消了操作,取消计时即可。还有,用户在点击或长按过程中,手指难免会移动,MotionEvent.ACTION_MOVE不可避免的会被触发,所以要在MotionEvent.ACTION_MOVE类型的事件中需要添加判断逻辑,移动超出一定范围才能取消。

最后,上代码:

public class MyView extends View {
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //计时器,计时点击时长
    Timer timer;
    TimerTask timerTask;

    boolean isCick=true;//判断是否进行点击
    private static final int LONGPRESSTIME= 300;//长按超过0.3秒,触发长按事件

    //记录上次点击的位置,用来进行移动的模糊处理
    int lastX=0;
    int lastY=0;

    //此处可以视为将View划分为10行10列的方格,在方格内移动看作没有移动。
    private static final int MOHUFANWEI=10;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        //实现获取点击位置
        float X = event.getX();
        float Y = event.getY();

        //手指移动的模糊范围,手指移动超出该范围则取消事件处理
        int length=getWidth()/MOHUFANWEI;
        final int indexX=(int)(Y/length);
        final int indexY=(int)(X/length);

        if (event.getAction() == MotionEvent.ACTION_DOWN
                && event.getPointerCount() == 1) {
            //长按计时器
            timer=new Timer();
            timerTask=new TimerTask() {
                @Override
                public void run() {
                    //长按逻辑触发,isClick置为false,手指移开后,不触发点击事件
                    isCick=false;
                    doLongPress(indexX,indexY);
                }
            };
            isCick=true;
            timer.schedule(timerTask,LONGPRESSTIME,1000*60*60*24);
        }

        if(event.getAction() == MotionEvent.ACTION_UP
                && event.getPointerCount() == 1)
        {
            //没有触发长按逻辑,进行点击事件
            if(isCick==true)
            {
                doClick(indexX,indexY);
            }
            //取消计时
            timerTask.cancel();
            timer.cancel();
        }

        //出现移动,取消点击和长按事件
        if(event.getAction() == MotionEvent.ACTION_MOVE)
        {
            //如果在一定范围内移动,不处理移动事件
            if(lastX==indexX&&lastY==indexY)
            {
                return true;
            }
            isCick=false;
            timerTask.cancel();
            timer.cancel();
        }

        //一旦触发事件,即改变上次触发事件的坐标
        lastY=indexY;
        lastX=indexX;
        return true;
    }
    private void doLongPress(int x,int y)
    {
        Log.e("CAM","长按了"+x+"   "+y);
    }

    private void doClick(int x,int y)
    {
        Log.e("CAM","点击了"+x+"   "+y);
    }
}自己琢磨的方法,可能还有很多的不足之处,如果有大佬看到,欢迎指正,谢谢。

你可能感兴趣的:(android)