Android 自定义View实现单击和双击事件的方法

作者:jingxian 时间:2022-03-23 19:21:18 

自定义View,

1. 自定义一个Runnable线程TouchEventCountThread ,  用来统计500ms内的点击次数

2. 在MyView中的 onTouchEvent 中调用 上面的线程

3. 自定义一个Handler, 在TouchEventHandler 中 处理 统计到的点击事件, 单击, 双击, 三击, 都可以处理

核心代码如下: 


public class MyView extends View {

......

// 统计500ms内的点击次数
 TouchEventCountThread mInTouchEventCount = new TouchEventCountThread();
 // 根据TouchEventCountThread统计到的点击次数, perform单击还是双击事件
 TouchEventHandler mTouchEventHandler = new TouchEventHandler();

@Override
 public boolean onTouchEvent(MotionEvent event) {
   switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN:
       if (0 == mInTouchEventCount.touchCount) // 第一次按下时,开始统计
         postDelayed(mInTouchEventCount, 500);
       break;
     case MotionEvent.ACTION_UP:
       // 一次点击事件要有按下和抬起, 有抬起必有按下, 所以只需要在ACTION_UP中处理
       mInTouchEventCount.touchCount++;
       // 如果是长按操作, 则Handler的消息,不能将touchCount置0, 需要特殊处理
       if(mInTouchEventCount.isLongClick) {
         mInTouchEventCount.touchCount = 0;
         mInTouchEventCount.isLongClick = false;
       }
       break;
     case MotionEvent.ACTION_MOVE:
       break;
     case MotionEvent.ACTION_CANCEL:
       break;
     default:
       break;
   }

return super.onTouchEvent(event);
 }

public class TouchEventCountThread implements Runnable {
   public int touchCount = 0;
   public boolean isLongClick = false;

@Override
   public void run() {
     Message msg = new Message();
     if(0 == touchCount){ // long click
       isLongClick = true;
     } else {
       msg.arg1 = touchCount;
       mTouchEventHandler.sendMessage(msg);
       touchCount = 0;
     }
   }
 }

public class TouchEventHandler extends Handler {

@Override
   public void handleMessage(Message msg) {
     Toast.makeText(mContext, "touch " + msg.arg1 + " time.", Toast.LENGTH_SHORT).show();
   }
 }

......

}

包装以后如下, 这样就能在别的地方调用了:


public interface OnDoubleClickListener{
   void onDoubleClick(View v);
 }

private OnDoubleClickListener mOnDoubleClickListener;

public void setOnDoubleClickListener(MyView.OnDoubleClickListener l) {
   mOnDoubleClickListener = l;
 }

public boolean performDoubleClick() {
   boolean result = false;
   if(mOnDoubleClickListener != null) {
     mOnDoubleClickListener.onDoubleClick(this);
     result = true;
   }
   return result;
 }

public class TouchEventHandler extends Handler {

@Override
   public void handleMessage(Message msg) {
     if(2 == msg.arg1)
       performDoubleClick();
   }
 }

在Activity中使用


myView1.setOnDoubleClickListener(new MyView.OnDoubleClickListener() {
 @Override
 public void onDoubleClick(View v) {
 Toast.makeText(mContext,"double click", Toast.LENGTH_SHORT).show();
 }
});

全部代码

MyView.java


package com.carloz.test.myapplication.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import com.carloz.test.myapplication.R;

/**
* Created by root on 15-11-9.
*/
public class MyView extends View {

private Paint mPaint = new Paint();
 private boolean mNotDestroy = true;
 private int mCount = 0;
 private MyThread myThread;
 Bitmap bitmap;
 // attrs
 private String mText;
 private boolean mStartChange;
 Context mContext;

public MyView(Context context) {
   super(context);
   init();
 }

public MyView(Context context, AttributeSet attrs) {
   super(context, attrs);
   TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView);
   mText = ta.getString(R.styleable.MyView_text);
   mStartChange = ta.getBoolean(R.styleable.MyView_startChange, false);
   // Log.d("ASDF", "mText=" + mText + ", mStartChange=" + mStartChange);
   ta.recycle();

init();
 }

@Override
 protected void onFinishInflate() {
   super.onFinishInflate();
 }

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 }

@Override
 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   super.onLayout(changed, left, top, right, bottom);
 }

@Override
 protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   mPaint.setTextSize(50);
   canvas.drawText(mText + mCount++, 20f, 100f, mPaint);
   canvas.save();
   canvas.rotate(60, getWidth() / 2, getHeight() / 2);
   canvas.drawBitmap(bitmap, 20f, 50f, mPaint);
   canvas.restore();

if (null == myThread) {
     myThread = new MyThread();
     myThread.start();
   }
 }

@Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
   return super.dispatchTouchEvent(ev);
 }

@Override
 protected void onAttachedToWindow() {
   super.onAttachedToWindow();
   mNotDestroy = true;
 }

@Override
 protected void onDetachedFromWindow() {
   mNotDestroy = false;
   super.onDetachedFromWindow();
 }

// 统计500ms内的点击次数
 TouchEventCountThread mInTouchEventCount = new TouchEventCountThread();
 // 根据TouchEventCountThread统计到的点击次数, perform单击还是双击事件
 TouchEventHandler mTouchEventHandler = new TouchEventHandler();

@Override
 public boolean onTouchEvent(MotionEvent event) {
   switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN:
       if (0 == mInTouchEventCount.touchCount) // 第一次按下时,开始统计
         postDelayed(mInTouchEventCount, 500);
       break;
     case MotionEvent.ACTION_UP:
       // 一次点击事件要有按下和抬起, 有抬起必有按下, 所以只需要在ACTION_UP中处理
       mInTouchEventCount.touchCount++;
       // 如果是长按操作, 则Handler的消息,不能将touchCount置0, 需要特殊处理
       if(mInTouchEventCount.isLongClick) {
         mInTouchEventCount.touchCount = 0;
         mInTouchEventCount.isLongClick = false;
       }
       break;
     case MotionEvent.ACTION_MOVE:
       break;
     case MotionEvent.ACTION_CANCEL:
       break;
     default:
       break;
   }

return super.onTouchEvent(event);
 }

public class TouchEventCountThread implements Runnable {
   public int touchCount = 0;
   public boolean isLongClick = false;

@Override
   public void run() {
     Message msg = new Message();
     if(0 == touchCount){ // long click
       isLongClick = true;
     } else {
       msg.arg1 = touchCount;
       mTouchEventHandler.sendMessage(msg);
       touchCount = 0;
     }
   }
 }

public class TouchEventHandler extends Handler {

@Override
   public void handleMessage(Message msg) {
     Toast.makeText(mContext, "touch " + msg.arg1 + " time.", Toast.LENGTH_SHORT).show();
   }
 }

class MyThread extends Thread {

@Override
   public void run() {
     super.run();
     while (mNotDestroy) {
       if (mStartChange) {
         postInvalidate();
         try {
           Thread.sleep(500);
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
       }
     }
   }
 }

public void init() {
   mContext = getContext();
   bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
 }

public void setText(String mText) {
   this.mText = mText;
 }

public void setStartChange(boolean mStartChange) {
   this.mStartChange = mStartChange;
 }

public boolean getStartChange() {
   return this.mStartChange;
 }
}

attrs.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="MyView">
   <attr name="text" format="string"/>
   <attr name="startChange" format="boolean"/>
 </declare-styleable>

</resources>

postDelayed方法最终是靠 Handler 的 postDelayed 方法 实现原理如下


public final boolean postDelayed(Runnable r, long delayMillis)
 {
   return sendMessageDelayed(getPostMessage(r), delayMillis);
 }

public final boolean sendMessageDelayed(Message msg, long delayMillis)
 {
   if (delayMillis < 0) {
     delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
   MessageQueue queue = mQueue;
   if (queue == null) {
     RuntimeException e = new RuntimeException(
         this + " sendMessageAtTime() called with no mQueue");
     Log.w("Looper", e.getMessage(), e);
     return false;
   }
   return enqueueMessage(queue, msg, uptimeMillis); // 然后在MessageQueue中会比较时间顺序
 }
标签:自定义,View,单击,双击
0
投稿

猜你喜欢

  • Java动态代理详解及实例

    2023-12-08 15:51:03
  • java判断某个点是否在所画多边形/圆形内

    2022-09-30 23:50:45
  • C#实现的Win32控制台线程计时器功能示例

    2022-12-19 20:29:20
  • Android 开发使用PopupWindow实现加载等待界面功能示例

    2023-03-26 07:51:36
  • 浅析Java中的内存泄漏

    2023-12-19 02:29:02
  • C#设置输入法实例分析

    2022-07-07 14:30:05
  • Spring JPA联表查询之注解属性详解

    2021-11-04 14:19:04
  • springboot bean循环依赖实现以及源码分析

    2022-06-05 11:50:15
  • 细谈java同步之JMM(Java Memory Model)

    2023-11-23 13:09:33
  • c#数据绑定之linq使用示例

    2022-07-17 11:07:22
  • Spring实现一个简单的SpringIOC容器

    2023-02-06 21:03:43
  • C#实现简单的计算器小功能

    2023-05-13 06:46:11
  • Java使用条件语句和循环结构确定控制流(实例)

    2022-07-04 04:52:35
  • Java与C++实现相同的MD5加密算法简单实例

    2023-08-31 09:43:02
  • @JsonFormat处理LocalDateTime失效的问题

    2023-07-22 18:41:13
  • Java中String类的常用方法总结

    2021-11-26 10:39:20
  • Spring Boot Logback配置日志过程解析

    2022-12-09 18:08:06
  • Java StringBuffer与StringBuilder有什么区别

    2022-12-15 22:35:12
  • 深入分析Java内存区域的使用详解

    2023-09-25 23:21:29
  • 详解JAVA中的OPTIONAL

    2022-08-22 17:09:04
  • asp之家 软件编程 m.aspxhome.com