自定义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实现单击和双击事件的更多相关文章

  1. easyui datagrid 自定义单元格单击与双击事件(Day_38)

    $(function(){ $('#tableId').datagrid({//单击事件   onClickRow:function(rowIndex,rowData){  alert("单 ...

  2. Android 自定义View——自定义点击事件

    每个人手机上都有通讯录,这是毫无疑问的,我们通讯录上有一个控件,在通讯录的最左边有一列从”#”到”Z”的字母,我们通过滑动或点击指定的字母来确定联系人的位置,进而找到联系人.我们这一节就通过开发这个控 ...

  3. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  4. [置顶] xamarin android自定义标题栏(自定义属性、回调事件)

    自定义控件的基本要求 这篇文章就当是自定义控件入门,看了几篇android关于自定义控件的文章,了解了一下,android自定义控件主要有3种方式: 自绘控件:继承View类,所展示的内容在OnDra ...

  5. (转)[原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  6. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  7. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

  8. [原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  9. Android自定义View

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

随机推荐

  1. linux中安装easy_install(setuptools)

    假设是相似于ubuntu的带桌面的系统直接下载安装就能够了.下面是针对centOS的命令行安装方法 最好先查看版本号号,依据版本号来选择安装方法.终端命令例如以下 # lsb_release -a 版 ...

  2. iotop,pt-ioprofile : mysql IO负载高的来源定位

    http://www.cnblogs.com/cenalulu/archive/2013/04/12/3016714.html 前言: 在一般运维工作中经常会遇到这么一个场景,服务器的IO负载很高(i ...

  3. 触摸与手势学习-swift

    触摸是一个UITouch对象,该对象放在一个UIEvent中,每个UIEvent包含一个或多个UITouch对象,每个UITouch对象对应一个手指.系统将UIEvent发送到应用程序上,最后应用程序 ...

  4. dateTimePicker日期时间插件-----限定节假日调休的可选择性

    需求:在项目中需要一款这样的日期插件,可以选择年月日,时分秒,对法定节假日不能选择,因法定节假日进行的调休可以选择: 现在使用的比较多的日期插件比如:Wdatepicker,jqueryUI的date ...

  5. Linux性能实时监测工具netdata安装配置

    netdata:功能强大的实时性能检测工具,展示地址. github地址:https://github.com/firehol/netdata 本文介绍在CentOS 6.7下安装netdata 1. ...

  6. php文件上传之单文件上传

    为了简单一些,php文件跟form表单写在了一个文件里. php单文件上传----> <!DOCTYPE html> <html> <head> <me ...

  7. mysql自增

    主键设置自增,同时主键需要是int类型

  8. 关于ASPOSE.WORD使用上的一个小问题

    最近实习期间负责了公司某个项目的一个功能模块里面的word导出功能,使用的是ASPOSE.WORD类库,但是经常导出时候会遇到图中的问题,大概意思就是两个表格不能跨在一起,调试了好几次还是没发现具体的 ...

  9. nyoj349 poj1094 Sorting It All Out(拓扑排序)

    nyoj349   http://acm.nyist.net/JudgeOnline/problem.php?pid=349poj1094   http://poj.org/problem?id=10 ...

  10. MySQL分区表例子——List分区

    列表分区(List分区) 这里假设表中有一个sale_item_type 字段,数据类型为INT 型 当sale_item_type 为1,3,5的时候,作为一个分区 当sale_item_type  ...