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

自定义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中会比较时间顺序
  }

以上就是小编为大家带来的Android 自定义View实现单击和双击事件的方法的全部内容了,希望对大家有所帮助,多多支持我们~

(0)

相关推荐

  • Android实现双击TitleBar回顶部的功能示例代码

    前言 本文介绍的内容是偶然发现的这个功能,就给移过来了,整理了一下,也是一个类就实现的,使用很方便 特别感谢@TakWolf大大的开源项目,学了好多Android方面的东西 双击返回顶部代码 public class DoubleClickBackToContentTopListener implements View.OnClickListener { private final long delayTime = 300; private long lastClickTime = 0; pri

  • Android 实现双击退出的功能

    实现android双击后退键退出当前APP功能 实现该功能基本思路是, 1, 监听后退键 , 比较两次后退间隔 , 低于两秒则出发退出 2, 退出当前APP 我选择在基类中BaseActivity 中设置监听,代码如下: public void onBackPressed() { //Preferences 中获取是否双击退出 boolean isDoubleClick = true; //BaseApplication.get("ifDoubleClickedBack", true)

  • Android App中实现可以双击放大和缩小图片功能的实例

    先来看一个很简单的核心图片缩放方法: public static Bitmap scale(Bitmap bitmap, float scaleWidth, float scaleHeight) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); Log.i(TAG, "s

  • Android双击返回键退出程序的实现方法

    本文实例讲述了Android双击返回键退出程序的实现方法,是Android程序开发中一个非常实用的功能,分享给大家供大家参考之用.具体方法如下: 一.实现思路: 用户按下返回键时设定一个定时器来监控是否2秒内实现了退出,如果用户没有接着按返回键,则清除第一次按返回键的效果,使程序还原到第一次按下返回键之前的状态.定时器是每次用户按下返回键才去创建. 二.功能代码: /** * 菜单.返回键响应 */ @Override public boolean onKeyDown(int keyCode,

  • Android中双击返回键退出应用实例代码

    Android中双击返回键退出程序 1.在MyAppliction中(继承Application) //运用list来保存们每一个activity是关键 private List<Activity> mList = new LinkedList<Activity>(); //为了实现每次使用该类时不创建新的对象而创建的静态对象 private static MyApplication instance; //构造方法 public MyApplication() { } //实例化

  • Android 双击返回键退出程序的方法总结

    Android 双击返回键退出程序的方法总结 下面先说说LZ思路,具体如下: 1. 第一种就是根据用户点击俩次的时间间隔去判断是否退出程序; 2. 第二种就是使用Android中计时器(Timer),其实这俩种都差不多. 思路是有了,,,接下来要怎么开搞呢???用户点击肯定会触发相应的事件,,,我们先来看下面俩个事件的作用... Activity.onKeyDown(); 当某个键被按下时会触发,但不会被任何的该Activity内的任何view处理. 默认按下KEYCODE_BACK键后会回到上

  • Android双击退出的实现方法

    本文实例讲述了Android双击退出的实现方法.分享给大家供大家参考.具体实现方法如下: 方式一: 重写onBackPressed方法直接监听返回键(建议高版本用2.0以上) 复制代码 代码如下: @Override  public void onBackPressed() {               long currentTime = System.currentTimeMillis();           if((currentTime-touchTime)>=waitTime) {

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

    自定义View, 1. 自定义一个Runnable线程TouchEventCountThread ,  用来统计500ms内的点击次数 2. 在MyView中的 onTouchEvent 中调用 上面的线程 3. 自定义一个Handler, 在TouchEventHandler 中 处理 统计到的点击事件, 单击, 双击, 三击, 都可以处理 核心代码如下: public class MyView extends View { ...... // 统计500ms内的点击次数 TouchEvent

  • Android自定义view之利用drawArc方法实现动态效果(思路详解)

    目录 前言 一.准备 1.测量 2.初始化画笔 3.自定义属性 二.关键方法介绍 drawArc 三.实现 1.思路 2.效果图 前言 前几天看了一位字节Android工程师的一篇博客,他实现的是歌词上下滚动的效果,实现的关键就是定义一个偏移量,然后根据情况去修改这个值,最后触发View的重绘来达到效果.于是今天根据这个思路来写一篇简单的文章.欢迎留言 一.准备 在这之前呢,还是得简单描述一下自定义view中的一些准备工作 1.测量 @Override protected void onSize

  • Android 自定义view模板并实现点击事件的回调

    Android 自定义view模板并实现点击事件的回调 主要的目的就是仿老版QQ的一个界面做一个模板.然后实现点击事件的回调.先看效果图: 步骤如下: 1.在res/values/目录下新建一个atts.xml文件 内容如下: <resources> <declare-styleable name="topbar"> <attr name="title" format="string"/> <attr n

  • Android 自定义view仿微信相机单击拍照长按录视频按钮

    Android仿微信相机的拍照按钮单击拍照,长按录视频.先上效果图. 项目地址:https://github.com/c786909486/PhotoButton2/tree/v1.0 添加依赖 allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { compile compile 'com.github.c786909486:PhotoButton2:v1.1' } 长按效果分

  • Android自定义View实现标签流效果

    本文实例为大家分享了Android自定义View实现标签流效果的具体代码,供大家参考,具体内容如下 一.概述 Android自定义View实现标签流效果,一行放不下时会自动换行,用户可以自己定义单个标签的样式,可以选中和取消,可以监听单个标签的点击事件,功能还算强大,可以满足大部分开发需求,值得推荐,效果图如下: 二.实现代码 1.自定义View 定义属性文件 <declare-styleable name="FlowTagView">         <attr n

  • Android 自定义View 密码框实例代码

    暴露您view中所有影响可见外观的属性或者行为. •通过XML添加和设置样式 •通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 效果图展示: 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="borde

  • Android自定义View详解

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 我把3用[]标出了,所以说3不一

  • Android自定义View 仿QQ侧滑菜单的实现代码

    先看看QQ的侧滑效果 分析一下 先上原理图(不知道能否表达的清楚 ==) -首先这里使用了 Android 的HorizontalScrollView 水平滑动布局作为容器,当然我们需要继承它自定义一个侧滑视图 - 这个容器里面有一个父布局(一般用LinerLayout,本demo用的是),这个父布局里面有且只有两个子控件(布局),初始状态菜单页的位置在Y轴上存在偏移这样可以就可以形成主页叠在菜单页的上方的视觉效果:然后在滑动的过程程中 逐渐修正偏移,最后菜单页和主页并排排列.原理搞清了实现起来

  • Android自定义View绘制随机生成图片验证码

    本篇文章讲的是Android自定义View之随机生成图片验证码,开发中我们会经常需要随机生成图片验证码,但是这个是其次,主要还是想总结一些自定义View的开发过程以及一些需要注意的地方. 按照惯例先看看效果图: 一.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure 4.重写onDraw 其中onMesure方法不一定要重写,但大部分情况下还是需要重写的 二.View 的几个构造函数 1.public CustomV

  • Android自定义View实现等级滑动条的实例

     Android自定义View实现等级滑动条的实例 实现效果图: 思路: 首先绘制直线,然后等分直线绘制点: 绘制点的时候把X值存到集合中. 然后绘制背景图片,以及图片上的数字. 点击事件down的时候,换小图片为大图片.move的时候跟随手指移动. up的时候根据此时的X计算最近的集合中的点,然后自动吸附回去. 1,自定义属性 <?xml version="1.0" encoding="utf-8"?> <resources> <de

随机推荐