Android系统中自定义按键的短按、双击、长按事件

在项目中碰到这样的问题: 
由于系统中的按键在底层做了重新定义或者新增了按键,此时需要在APP层对按键事件(keyevent)做分解处理,模拟Android系统做法,把keyevent分解成: 
1、单击事件:就是普通key的单击; 
2、双击事件:500ms内同一按键单击两次; 
3、长按事件:同一按键长按超过1000ms(系统中长按事件为500ms); 
4、组合按键:两个以上按键同时按住;

其中的keyevent可以来自Activity、View子类的dispatchKeyEvent方法,也可以是我们自定义的接口,也可以是我们发广播送上来的,根据项目需求;

关于各事件的原理: 
1、双击事件:每次点击的up事件中启动一个定时(500ms)线程消息,用Handler.postDelayed()方法。 
2、长按事件:每次点击的down事件中启动一个定时(1000ms)线程消息,用Handler.postDelayed()方法,注意:在RepeatCount==0时启动; 
3、组合按键:用变量记录每个按键的状态,再进行判断;

具体代码如下: Java代码

package com.jerome.util;

import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;

public class KeyUtil {
	private boolean isVolumeDown = false;
	private boolean isVolumeUp = false;
	private boolean isMenu = false;
	private int currentKeyCode = 0;

	private static Boolean isDoubleClick = false;
	private static Boolean isLongClick = false;

	CheckForLongPress mPendingCheckForLongPress = null;
	CheckForDoublePress mPendingCheckForDoublePress = null;
	Handler mHandler = new Handler();

	Context mContext = null;
	private String TAG = "";

	public KeyUtil(Context context, String tag) {
		mContext = context;
		TAG = tag;
	}

	public void dispatchKeyEvent(KeyEvent event) {
		int keycode = event.getKeyCode();

		// 有不同按键按下,取消长按、短按的判断
		if (currentKeyCode != keycode) {
			removeLongPressCallback();
			isDoubleClick = false;
		}

		// 处理长按、单击、双击按键
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			checkForLongClick(event);
		} else if (event.getAction() == KeyEvent.ACTION_UP) {
			checkForDoubleClick(event);
		}

		if (keycode == KeyEvent.KEYCODE_VOLUME_DOWN) {
			if (event.getAction() == KeyEvent.ACTION_DOWN) {
				isVolumeDown = true;
			} else if (event.getAction() == KeyEvent.ACTION_UP) {
				isVolumeDown = false;
			}
		} else if (keycode == KeyEvent.KEYCODE_VOLUME_UP) {
			if (event.getAction() == KeyEvent.ACTION_DOWN) {
				isVolumeUp = true;
			} else if (event.getAction() == KeyEvent.ACTION_UP) {
				isVolumeUp = false;
			}
		} else if (keycode == KeyEvent.KEYCODE_MENU) {
			if (event.getAction() == KeyEvent.ACTION_DOWN) {
				isMenu = true;
			} else if (event.getAction() == KeyEvent.ACTION_UP) {
				isMenu = true;
			}
		}

		// 判断组合按键
		if (isVolumeDown
				&& isVolumeUp
				&& isMenu
				&& (keycode == KeyEvent.KEYCODE_VOLUME_UP
						|| keycode == KeyEvent.KEYCODE_VOLUME_DOWN || keycode == KeyEvent.KEYCODE_MENU)
				&& event.getAction() == KeyEvent.ACTION_DOWN) {
			//组合按键事件处理;
			isVolumeDown = false;
			isVolumeUp = false;
			isMenu = false;
		}
	}

	private void removeLongPressCallback() {
		if (mPendingCheckForLongPress != null) {
			mHandler.removeCallbacks(mPendingCheckForLongPress);
		}
	}

	private void checkForLongClick(KeyEvent event) {
		int count = event.getRepeatCount();
		int keycode = event.getKeyCode();
		if (count == 0) {
			currentKeyCode = keycode;
		} else {
			return;
		}
		if (mPendingCheckForLongPress == null) {
			mPendingCheckForLongPress = new CheckForLongPress();
		}
		mPendingCheckForLongPress.setKeycode(event.getKeyCode());
		mHandler.postDelayed(mPendingCheckForLongPress, 1000);
	}

	class CheckForLongPress implements Runnable {

		int currentKeycode = 0;

		public void run() {
			isLongClick = true;
			longPress(currentKeycode);
		}

		public void setKeycode(int keycode) {
			currentKeycode = keycode;
		}
	}

	private void longPress(int keycode) {
		Log.i(TAG, "--longPress 长按事件--" + keycode);
	}

	private void singleClick(int keycode) {
		Log.i(TAG, "--singleClick 单击事件--" + keycode);
	}

	private void doublePress(int keycode) {
		Log.i(TAG, "---doublePress 双击事件--" + keycode);
	}

	private void checkForDoubleClick(KeyEvent event) {
		// 有长按时间发生,则不处理单击、双击事件
		removeLongPressCallback();
		if (isLongClick) {
			isLongClick = false;
			return;
		}

		if (!isDoubleClick) {
			isDoubleClick = true;
			if (mPendingCheckForDoublePress == null) {
				mPendingCheckForDoublePress = new CheckForDoublePress();
			}
			mPendingCheckForDoublePress.setKeycode(event.getKeyCode());
			mHandler.postDelayed(mPendingCheckForDoublePress, 500);
		} else {
			// 500ms内两次单击,触发双击
			isDoubleClick = false;
			doublePress(event.getKeyCode());
		}
	}

	class CheckForDoublePress implements Runnable {

		int currentKeycode = 0;

		public void run() {
			if (isDoubleClick) {
				singleClick(currentKeycode);
			}
			isDoubleClick = false;
		}

		public void setKeycode(int keycode) {
			currentKeycode = keycode;
		}
	}

	private void removeDoublePressCallback() {
		if (mPendingCheckForDoublePress != null) {
			mHandler.removeCallbacks(mPendingCheckForDoublePress);
		}
	}
}

  注意: 
只有Action Down状态下RepeatCount才会>0,避免长按和单击事件混淆;

时间: 2024-10-20 19:59:27

Android系统中自定义按键的短按、双击、长按事件的相关文章

Android开发如何在4.0及以上系统中自定义TitleBar

本文将通过一个实例讲解怎么实现在4.0及以上系统版本中实现自定义TitleBar,这只是我自己找到的一种方法; xml布局文件 activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="m

Android系统中的广播(Broadcast)机制简要介绍和学习计划

在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个简单的例子来学习Android系统的广播机制,为后续分析广播机制的源代码作准备. 在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式

Android系统中设置TextView的行间距(非行高)

Android系统中TextView默认显示中文时会比较紧凑,不是很美观.为了让每行保持一定的行间距,可以设置属性android:lineSpacingExtra或android:lineSpacingMultiplier. 关于Android下TextView中文换行问题,可查看Android自定义view-文本自动换行. 1.android:lineSpacingExtra设置行间距,如”3dp”. 2.android:lineSpacingMultiplier设置行间距的倍数,如”1.2″

Android 系统中,那些能大幅提高工作效率的 API 汇总(持续更新中...)

前言 "条条大路通罗马."工作中,实现某个需求的方式往往不是唯一的,这些不同实现方式不仅表现在代码质量上,还影响着我们的工作效率.就像,在 Android 系统中,总有那么一些鲜为人知的 API 能够减少我们很多零碎的工作量.于是,就想凭着一些经验,整理一些常用的,找个地方归纳总结,也供日后翻阅. getResources().getIdentifier(String name, String defType, String defPackage) 根据资源名称获取资源 id.正常情况

Android -- 系统和自定义Notification

Notification是一种让你的应用程序在不使用Activity的情况下警示用户,Notification是看不见的程序组件警示用户有需要注意的事件发生的最好途径. 作为UI部分,Notification对移动设备来说是最适合不过的了.用户可能随时都带着手机在身边.一般来说,用户会在后台打开几个程序,但不会注意它们.在这样的情形下,当发生需要注意的事件时,能够通知用户是很重要的. Notification由NotificationManger统一管理,目前包含的能力有: 创建一个状态条图标.

AIDL在android系统中的作用

AIDL,Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口.最近看了下AIDL在Android系统中的用法,在网上看到很多初学的朋友不太明白AIDL的实际作用,android提供了很多进程间通信的组件,像action.broadcast.contentprovide都可以实现进程间的通信,为什么还要用AIDL这个东西呢?我在android源码中实现了一个自己写的AIDL例子,用以

Android系统中添加一个产品----图文详解

本文本着开源的精神介绍如何向一个Android系统中添加一个产品的整个过程,按照以下过程笔者有理由相信每个将要从事本行业的人都可以完成,其实添加一个产品并不难,难的是对其相关硬件的修改,好了废话不多说. 首先我们要创建一个属于自己产品的目录,这里以WY_device为例,以WY作为产品的名字. 首先从已经存在的产品中拷贝一个以产品的名字为名的.mk文件,修改为自己的.mk文件,在这里为WY.mk 对其进行如下的修改: 然后添加AndroidProducts.mk  这是添加产品的配置文件名路径,

Android系统中的dp和px的转换

android系统中DP和SP的转化:1.首先分析TypedValue.java 可以调用以下代码获得dp的值 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20,getResources().getDisplayMetrics()); public static float applyDimension(int unit, float value,DisplayMetrics metrics) { switch (unit) {

自己动手清除Android系统中的不良程序

陈跃峰 2014/6/29 最近在使用我的Nexus7时,总是经常莫名其妙的弹出一些广告弹窗,还会自动下载一些应用程序,还会在桌面上生成一个叫做"精彩应用"的图标,这个快捷方式关联的程序显示的内容和广告内容是一致的,很是烦人,使用360手机卫士和乐安全都发现不了这个不良程序,所以就自己动手来清除这个程序. 由于Nexus 7平时用于测试程序,安装的应用很多,手动删除了一些可疑的应用以后还是不行,猜测是某些app可能被人篡改了,就不再去删除app了,而把目光转向了桌面快捷方式上. 既然生