Android View 事件分发机制,看这一篇就够了(二)

简介: Android View 事件分发机制,看这一篇就够了

外部解决法


从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截,不要的时候返回false,为代码大概 如下


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    final float x = ev.getX();
    final float y = ev.getY();
    final int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            mDownPosX = x;
            mDownPosY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            final float deltaX = Math.abs(x - mDownPosX);
            final float deltaY = Math.abs(y - mDownPosY);
            // 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截
            if (deltaX > deltaY) {
                return false;
            }
    }
    return super.onInterceptTouchEvent(ev);
}


内部解决法


从子View着手,父View先不要拦截任何事件,所有的事件传递给 子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View处理。


实现思路 如下,重写子 View的dispatchTouchEvent方法,在Action_down 动作中通过方法 requestDisallowInterceptTouchEvent(true) 先请求 父 View不要拦截事件,这样保证子 View 能够接受到 Action_move 事件,再在 Action_move 动作中根据自己的逻辑是否要拦截事件,不需要拦截事件的话再交给 父 View 处理。


@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    int x = (int) ev.getRawX();
    int y = (int) ev.getRawY();
    int dealtX = 0;
    int dealtY = 0;
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            dealtX = 0;
            dealtY = 0;
            // 保证子View能够接收到Action_move事件
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            dealtX += Math.abs(x - lastX);
            dealtY += Math.abs(y - lastY);
            Log.i(TAG, "dealtX:=" + dealtX);
            Log.i(TAG, "dealtY:=" + dealtY);
            // 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截
            if (dealtX >= dealtY) {
                getParent().requestDisallowInterceptTouchEvent(true);
            } else {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            lastX = x;
            lastY = y;
            break;
        case MotionEvent.ACTION_CANCEL:
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.dispatchTouchEvent(ev);
}


更多细节,可以查看我的这篇文章,里面有详细介绍哦

ViewPager,ScrollView 嵌套ViewPager滑动冲突解决


View 双击,多击事件是怎么实现的


实现之前,我们首先来阐述一下思路,怎样实现双击事件,正所谓,授人以鱼不如授人以渔。


单击:用户点击一次之后,一段时间之内不再点击


双击;用户点击一次之后,一段时间之内再次点击


实现思路


  1. 我们监听 onTouch 事件,在 ACTION_DOWN 的时候,点击次数 clickCount +1;
  2. 同时,在 ACTION_DOWN 的时候,延时一段时间,执行相应的 Runnable 任务,这里我们用 handler 的 postDelayed 实现
  3. 在延时任务执行的时候,我们根据点击的次数,进行单击或者多级的回调,最后,记得重置点击次数,以及移除延时任务


open class MyDoubleTouchListener(private val myClickCallBack: MyClickCallBack) : OnTouchListener {
    private var clickCount = 0 //记录连续点击次数
    private val handler: Handler = Handler()
    interface MyClickCallBack {
        fun oneClick() //点击一次的回调
        fun doubleClick() //连续点击两次的回调
    }
    override fun onTouch(v: View, event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
            clickCount++
            handler.postDelayed({
                if (clickCount == 1) {
                    myClickCallBack.oneClick()
                } else if (clickCount == 2) {
                    myClickCallBack.doubleClick()
                }
                handler.removeCallbacksAndMessages(null)
                //清空handler延时,并防内存泄漏
                clickCount = 0 //计数清零
            }, timeout.toLong()) //延时timeout后执行run方法中的代码
        }
        return false //让点击事件继续传播,方便再给View添加其他事件监听
    }
    companion object {
        private const val TAG = "MyClickListener"
        private val timeout = ViewConfiguration.getDoubleTapTimeout() //双击间四百毫秒延时
        init {
            Log.i(TAG, "timeout is $timeout ")
        }
    }
}


三击事件


三级事件呢,其实也很简单,我们直接判断在指定时间间隔内点击的次数即可


open class MyMultiTouchListener(private val myClickCallBack: MyClickCallBack) : OnTouchListener {
    private var clickCount = 0 //记录连续点击次数
    private val handler: Handler = Handler()
    interface MyClickCallBack {
        fun oneClick() //点击一次的回调
        fun doubleClick() //连续点击两次的回调
        fun threeClick() // 连续点击三次的回调
    }
    override fun onTouch(v: View, event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
            clickCount++
            handler.postDelayed({
                if (clickCount == 1) {
                    myClickCallBack.oneClick()
                } else if (clickCount == 2) {
                    myClickCallBack.doubleClick()
                } else if (clickCount == 3) {
                    myClickCallBack.threeClick()
                }
                handler.removeCallbacksAndMessages(null)
                //清空handler延时,并防内存泄漏
                clickCount = 0 //计数清零
            }, timeout.toLong()) //延时timeout后执行run方法中的代码
        }
        return false //让点击事件继续传播,方便再给View添加其他事件监听
    }
    companion object {
        private const val TAG = "MyClickListener"
        private val timeout = 600 //双击间四百毫秒延时
        init {
            Log.i(TAG, "timeout is $timeout ")
        }
    }
}

手势识别


在 Android 开发当中,几乎所有的事件都会与用户进行交互,而我们用得的最多的就是手势了。


我们知道当我们触摸屏幕的时候,会产生很多事件,比如 down,move,up, fling 事件等等。一些简单的处理,我们可以直接重写 View 的 onTouchEvent 方法,根据 View 的 MotionEvent 事件进行处理。


而 Google 为了方便开发者方便接入,提供了几个默认处理类,那就是 GestureDetector 和 ScaleGestureDetector。


GestureDetector这个类对外提供了两个接口和一个外部类

接口:OnGestureListener,OnDoubleTapListener


内部类:SimpleOnGestureListener,同时实现了 OnGestureListener,OnDoubleTapListener 接口,如果只想使用接口里面的某个方法,可以直接使用它,方便快捷。


讲解之前,我们向来看一下怎么使用


GestureDetector(Context context, GestureDetector.OnGestureListener listener)


GestureDetector 基本使用


第一步,初始化 GestureDetector 对象


mDetector = GestureDetectorCompat(this, MyGestureListener())


可以看到有两个参数,第一个参数 context,第二个参数 OnGestureListener,我们可以直接实现 OnGestureListener 接口,也可以直接使用 GestureDetector.SimpleOnGestureListener


private class MyGestureListener : GestureDetector.OnGestureListener {
        private val TAG = "GestureDemoActivity"
        override fun onShowPress(e: MotionEvent?) {
            Log.d(TAG, "onShowPress: e is $e")
        }
        override fun onSingleTapUp(e: MotionEvent?): Boolean {
            Log.d(TAG, "onSingleTapUp: e is $e")
            return false
        }
        override fun onDown(event: MotionEvent): Boolean {
            Log.d(TAG, "onDown: $event")
            return true
        }
        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(TAG, "onFling: $event1 $event2")
            return false
        }
        override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
            Log.d(TAG, "onScroll: distanceX is $distanceX,distanceY is $distanceY ")
            return false
        }
        override fun onLongPress(e: MotionEvent?) {
            Log.d(TAG, "onLongPress: e is $e")
        }
    }


第二步:设置双击监听


// 设置双击监听
mDetector.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
            override fun onDoubleTap(e: MotionEvent?): Boolean {
                Log.d(TAG, "onDoubleTap: e is e")
                return false
            }
            override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
                Log.d(TAG, "onDoubleTapEvent: e is e")
                return false
            }
            override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
                Log.d(TAG, "onSingleTapConfirmed: e is e")
                return false
            }
        })


最后,重写 Activity 或者 View 的 onTouchEvent ,将事件交给 mDetector 处理。


通常会有两种写法,第一种是如果手势处理器处理了,直接返回 true,进行消费。否则,进行默认处理


override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }


第二种写法是直接在 onTouchEvent 方法中,直接调用 mDetector.onTouchEvent(event) 方法


override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }


第二种写法,一般不会影响当前 View 或者 Activity 事件的传递,在开发当中,有时候为了减少一些触摸事件的冲突,经常这样写。


ScaleGestureDetector 这里暂时不展开描述了了,写着写着,发现好多呀,一个周末就这样过去,贼快,觉得对你有帮助的,请来个三连,点赞,收藏,转发😆。


小结


这篇文章,其实不难。主要是将 View 的事件分发机制,滑动冲突,以及开发当中经常用到的一些知识点,总结一下。


参考博客:


图解 Android 事件分发机制

ViewPager,ScrollView 嵌套ViewPager滑动冲突解决

Demo 地址



相关文章
|
14天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
28 5
|
22天前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
22天前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
30 2
|
23天前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
23天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
26天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
24 2
|
Android开发
Android Touch事件分发(源码分析)
Android一文让你轻松搞定Touch事件分发 源码分析 Activity事件分发机制 Activity.dispatchTouchEvent()源码 Activity.onTouchEvent()源码 Activity源码总结 ViewGroup事件分发机制 ViewGroup.dispatchTouchEvent()源码 ViewGroup.onInterceptTouchEvent()源码 ViewGroup.onTouchEvent()源码 ViweGroup源码总结 View的事件分发机制 View.dispatchTouchEvent()源码
180 0
Android Touch事件分发(源码分析)
|
Java Android开发
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
233 0
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
|
Java Android开发
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )(二)
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )(二)
159 0
|
Android开发
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )(四)
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )(四)
217 0