为了实现对Android任意控件和按键进行双击事件的监听,所以写了一个可以在代码中任意移植的解决方法。
一、解决思路
为了实现双击事件的监听,首先需要一个线程来执行两次点击事件的判断,决定是否属于一次双击事件,为了可以实现多次监听,所以在线程中是一个死循环,避免开无数个线程;然后将判断结果通过handler传递出去,由handler对message中携带的boolean数据进行判断,决定回调双击事件监听器中的双击或单击方法,监听器中的事件由用户覆写;最后在自己想要添加双击事件监听的控件或按键的点击事件监听器中创建一个双加事件管理器并启动就可以了。
二、准备工作
1.创建双击事件监听器接口
- /**
- * Created by 明月春秋 on 2017/11/18.
- * 负责对是否双击事件结果的回调监听器
- * 方法:
- * 1.onSinglePress:用于处理单击事件
- * 2.onDoublePress:用于处理双击事件
- */
- public interface OnDoubleClickListener {
- /**
- * 用于处理单击事件
- */
- public void onSinglePress();
- /**
- * 用于处理双击事件
- */
- public void onDoublePress();
- }
2.创建双击事件处理handler,用来决定是执行监听器中的双击事件还是单击事件
- /**
- * Created by 明月春秋 on 2017/11/18.
- * 负责处理双击事件的handler
- * 方法:
- * 1.setDoubleRunnable:设置handler处理所在的线程执行体
- */
- public class DoubleHandler extends Handler {
- private OnDoubleClickListener mListener; //对是否双击结果的回调监听器
- private DoubleRunnable mDoubleRunnable; //负责双击事件的线程执行体
- /**
- * 双击事件处理器的构造函数
- * @param listener
- * 对是否双击结果的回调监听器
- */
- public DoubleHandler(OnDoubleClickListener listener) {
- super(Looper.getMainLooper());
- this.mListener = listener;
- }
- @Override public void handleMessage(Message msg) {
- if ((boolean) msg.obj) {
- mListener.onDoublePress();
- } else {
- mListener.onSinglePress();
- }
- //通知双击事件线程执行体,事件已处理完
- mDoubleRunnable.setIsStarted(false);
- }
- /**
- * 设置handler处理所在的线程执行体
- * @param doubleRunnable
- * 传入的当前线程执行体
- */
- public void setDoubleRunnable(DoubleRunnable doubleRunnable) {
- this.mDoubleRunnable = doubleRunnable;
- }
- }
3.创建执行双击事件判断的线程执行体,在其中进行死循环,一直等待用户的点击,并对相关运行状态进行判断,将双击事件的判断结果发送给前面的DoubleHandler 进行处理
- /**
- * Created by 明月春秋 on 2017/11/18.
- * 负责双击事件的线程执行体
- * 方法:
- * 1.setPreparedState:设置当前双击事件的执行状态
- * 2.setIsStarted:设置线程是否处于一次双击事件判断的阻塞之中
- * 3.setDoubleSpan:设置用于判断双击的间隔时间
- */
- public class DoubleRunnable implements Runnable {
- private DoubleHandler mHandler; //处理点击事件的handler
- private boolean isStarted = false; //表示线程是否正处于阻塞中
- private boolean isPrepared = false; //表示是否已准备好启动线程
- private boolean isDoubleClicked = false; //表示是否被双击
- private int doubleSpan; //用于判断双击的间隔时间
- /**
- * 双击事件线程执行体的构造函数
- * @param handler
- * 用于处理是否双击事件的handler
- */
- public DoubleRunnable(DoubleHandler handler) {
- handler.setDoubleRunnable(this);
- this.mHandler = handler;
- }
- @Override public void run() {
- while (true) {
- //如果没有被点击,则继续下次循环
- if (!isPrepared) {
- continue;
- }
- isPrepared = false;
- //如果线程处于一次双击事件的阻塞中,则继续下次循环,直至事件结束
- if (isStarted) {
- continue;
- }
- isStarted = true;
- try {
- Thread.sleep(doubleSpan);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- Message msg = mHandler.obtainMessage();
- msg.obj = isDoubleClicked;
- mHandler.sendMessage(msg);
- }
- }
- /**
- * 设置当前双击事件的执行状态
- * @param isPrepared
- * 是否已被点击(可以执行线程中代码)
- * @param isDoubleClicked
- * 是否已被双击
- */
- public void setPreparedState(boolean isPrepared, boolean isDoubleClicked) {
- this.isPrepared = isPrepared;
- this.isDoubleClicked = isDoubleClicked;
- }
- /**
- * 设置线程是否处于一次双击事件判断的阻塞之中
- * @param isStarted
- * 传入是否阻塞开启
- */
- public void setIsStarted(boolean isStarted) {
- this.isStarted = isStarted;
- }
- /**
- * 设置用于判断双击的间隔时间
- * @param doubleSpan
- * 传入的时间间隔
- */
- public void setDoubleSpan(int doubleSpan) {
- this.doubleSpan = doubleSpan;
- }
- }
4.最后创建一个双击事件管理器,进行线程的创建管理,并用于启动线程和更新用户的点击时间,之所以在管理器中更新时间并进行判断,是因为可以随时根据用户的点击去更新DoubleRunnable中的双击判断结果,如果在DoubleRunnable中进行时间判断,则会因为判断只会在下一次阻塞循环结束之后才能进行,导致一直是单击事件执行。
- /**
- * Created by 明月春秋 on 2017/11/18.
- * 双击事件的管理器
- * 方法:
- * 1.start:开启双击事件的线程执行
- */
- public class DoubleManager {
- private DoubleRunnable mDoubleRunnable = null; //负责双击事件的线程执行体
- private Thread mThread = null; //负责双击事件的线程
- private long mClickTime = 0; //记录点击时间
- private static final int DOUBLE_SPAN = 300; //用于判断双击的间隔时间
- /**
- * 双击事件管理器的构造函数
- * @param doubleRunnable
- * 传入负责双击事件的线程执行体
- */
- public DoubleManager(DoubleRunnable doubleRunnable) {
- if (doubleRunnable == null) {
- return;
- }
- if (mDoubleRunnable == null) {
- doubleRunnable.setDoubleSpan(DOUBLE_SPAN);
- this.mDoubleRunnable = doubleRunnable;
- }
- }
- /**
- * 开启双击事件的线程执行
- */
- public void start() {
- if (mDoubleRunnable == null) {
- return;
- }
- boolean isDoubleClicked = false;
- if ((System.currentTimeMillis() - mClickTime) < DOUBLE_SPAN) {
- isDoubleClicked = true;
- } else {
- isDoubleClicked = false;
- mClickTime = System.currentTimeMillis();
- }
- mDoubleRunnable.setPreparedState(true, isDoubleClicked);
- if (mThread == null) {
- mThread = new Thread(mDoubleRunnable);
- mThread.start();
- }
- }
- }
5.接下来就可以在任意控件的单击事件监听器中或按键事件中,进行双击事件的判断了。
- public class DoubleClick extends AppCompatActivity {
- private DoubleManager doubleManager = null;
- private DoubleManager keyDoubleManager = null;
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.button_double_click);
- }
- public void doubleClick(View view){
- if (doubleManager == null){
- doubleManager = new DoubleManager(new DoubleRunnable(new DoubleHandler(new OnDoubleClickListener() {
- @Override
- public void onSinglePress() {
- Toast.makeText(DoubleClick.this, "单击", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onDoublePress() {
- Toast.makeText(DoubleClick.this, "双击", Toast.LENGTH_SHORT).show();
- }
- })));
- }
- doubleManager.start();
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_ENTER){
- if (keyDoubleManager == null){
- keyDoubleManager = new DoubleManager(new DoubleRunnable(new DoubleHandler(new OnDoubleClickListener() {
- @Override
- public void onSinglePress() {
- Toast.makeText(DoubleClick.this, "按键单击", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onDoublePress() {
- Toast.makeText(DoubleClick.this, "按键双击", Toast.LENGTH_SHORT).show();
- }
- })));
- }
- keyDoubleManager.start();
- }
- return super.onKeyDown(keyCode, event);
- }
- }
运行例子中按键事件时,记得将Button在布局中设为android:visibility="gone",防止按键事件直接点击到按钮上,执行按钮的双击事件监听了。
三、运行效果图
1.执行按钮单击
2.执行按钮双击
3.执行按键单击
4.执行按键双击
四、总结
到此,Android任意控件或按键的双击事件监听就已经全部实现了。只要前面将准备工作做好,就可以随意在任何代码处添加双击事件管理器并执行了,不会对原有代码产生任何影响,如果对本文有什么意见和问题欢迎指正,如果觉得本文可以,请帮忙点个赞,谢谢观看。
来源: https://juejin.im/post/5a12db7b6fb9a045030fb20e