一次触摸事件,ACTION_DOWN和ACTION_UP是必须存在的,ACTION_MOVE视情况而定。
虽然viewGroup是view的子类,这里的view指除去viewGroup的view控件,例如textView,button,imageView等控件 写个简单的demo,分析view的事件传递
class MyTextView : androidx.appcompat.widget.AppCompatTextView {
constructor(context: Context):super(context){
}
constructor(context: Context, attributeSet: AttributeSet): super(context, attributeSet){
}
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int): super(context, attributeSet, defStyleAttr){
}
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MyTextView","dispatchTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MyTextView","dispatchTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MyTextView","dispatchTouchEvent ACTION_MOVE")
}
}
return super.dispatchTouchEvent(event)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MyTextView","onTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MyTextView","onTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MyTextView","onTouchEvent ACTION_MOVE")
}
}
return super.onTouchEvent(event)
}
}class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var mTextView = findViewById<MyTextView>(R.id.mTextView)
mTextView.setOnClickListener {
Log.e("ysl","mTextView Click")
}
mTextView.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("mTextView","OnTouch ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("mTextView","OnTouch ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("mTextView","OnTouch ACTION_MOVE")
}
}
return@setOnTouchListener super.onTouchEvent(event)
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MainActivity","dispatchTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MainActivity","dispatchTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MainActivity","dispatchTouchEvent ACTION_MOVE")
}
}
return super.dispatchTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MainActivity","onTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MainActivity","onTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MainActivity","onTouchEvent ACTION_MOVE")
}
}
return super.onTouchEvent(event)
}
}2021-03-30 18:07:14.880 23744-23744/com.ysl.dispatchstudy E/MainActivity: dispatchTouchEvent ACTION_DOWN
2021-03-30 18:07:14.880 23744-23744/com.ysl.dispatchstudy E/MyTextView: dispatchTouchEvent ACTION_DOWN
2021-03-30 18:07:14.880 23744-23744/com.ysl.dispatchstudy E/mTextView: OnTouch ACTION_DOWN
2021-03-30 18:07:14.880 23744-23744/com.ysl.dispatchstudy E/MyTextView: onTouchEvent ACTION_DOWN
2021-03-30 18:07:14.960 23744-23744/com.ysl.dispatchstudy E/MainActivity: dispatchTouchEvent ACTION_UP
2021-03-30 18:07:14.960 23744-23744/com.ysl.dispatchstudy E/MyTextView: dispatchTouchEvent ACTION_UP
2021-03-30 18:07:14.960 23744-23744/com.ysl.dispatchstudy E/mTextView: OnTouch ACTION_UP
2021-03-30 18:07:14.960 23744-23744/com.ysl.dispatchstudy E/MyTextView: onTouchEvent ACTION_UP
2021-03-30 18:07:14.961 23744-23744/com.ysl.dispatchstudy E/ysl: mTextView Clickview的事件传递 根据结果显示 1、触摸事件的传递流程是从dispatchTouchEvent开始的,如果没有人为干预(也就是默认返回父类的同名函数),则事件将会按照嵌套层次有外向内传递,到达最内层的view时,就由最内层的onTouchEvent进行处理,如果能处理就返回true消费掉,如果不能处理就返回false,这时事件会重新向外层传递,并由外层的onTouchEvent进行处理,依次类推 2、如果事件在向内层传递过程中被人为干预,事件处理函数返回true,事件将会被提前消费掉,内层view将不会收到这个事件 3、view的事件触发是先执行onTouch方法,在最后执行onClick方法,如果onTouch返回true,事件将不会继续传递,最后也不会调用onClick方法,如果返回false,事件继续传递
viewGroup作为view控件的容器存在,Android系统默认提供了一系列viewGroup,例如LinearLayout,FrameLayout,RelativeLayout,ListView等
class MyRelativeLayout :RelativeLayout{
constructor(context: Context):super(context){
}
constructor(context: Context, attributeSet: AttributeSet): super(context, attributeSet){
}
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int): super(context, attributeSet, defStyleAttr){
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MyRelativeLayout","dispatchTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MyRelativeLayout","dispatchTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MyRelativeLayout","dispatchTouchEvent ACTION_MOVE")
}
}
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MyRelativeLayout","onInterceptTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MyRelativeLayout","onInterceptTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MyRelativeLayout","onInterceptTouchEvent ACTION_MOVE")
}
}
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("MyRelativeLayout","onTouchEvent ACTION_DOWN")
}
MotionEvent.ACTION_UP -> {
Log.e("MyRelativeLayout","onTouchEvent ACTION_UP")
}
MotionEvent.ACTION_MOVE -> {
Log.e("MyRelativeLayout","onTouchEvent ACTION_MOVE")
}
}
return super.onTouchEvent(event)
}
}2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/MainActivity: dispatchTouchEvent ACTION_DOWN
2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/MyTextView: dispatchTouchEvent ACTION_DOWN
2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/mTextView: OnTouch ACTION_DOWN
2021-03-30 18:17:56.680 24022-24022/com.ysl.dispatchstudy E/MyTextView: onTouchEvent ACTION_DOWN
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/MainActivity: dispatchTouchEvent ACTION_UP
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/MyRelativeLayout: dispatchTouchEvent ACTION_UP
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/MyRelativeLayout: onInterceptTouchEvent ACTION_UP
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/MyTextView: dispatchTouchEvent ACTION_UP
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/mTextView: OnTouch ACTION_UP
2021-03-30 18:17:56.760 24022-24022/com.ysl.dispatchstudy E/MyTextView: onTouchEvent ACTION_UP
2021-03-30 18:17:56.761 24022-24022/com.ysl.dispatchstudy E/ysl: mTextView Click根据日志打印结果可以看出 1、触摸事件的传递顺序是activity->viewGroup->view 2、viewGroup通过onInterceptTouchEvent方法对事件进行拦截 true 则事件不会传递给子view false货super.onInterceptTouchEvent,事件会继续传递给子view 3、在子view中对事件进行了消费,viewGroup将接受不到任何事件\
当我们内外两层View都可以滑动时候,就会产生滑动冲突。
//伪代码
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
var intercepted = false
when (ev?.getAction()) {
MotionEvent.ACTION_DOWN -> {
intercepted = false
}
MotionEvent.ACTION_MOVE -> {
intercepted = 满足父容器的拦截要求
}
MotionEvent.ACTION_UP -> {
intercepted = false
}
else -> {
}
}
return intercepted
}a、根据业务逻辑需要,在ACTION_MOVE方法中进行判断,如果需要父View处理则返回true,否则返回false,事件分发给子View去处理 b、ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE 与 ACTION_UP事件都将默认交给父View去处理 c、原则上ACTION_UP也需要返回false,如果返回true,并且滑动事件交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也无法触发。而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理
//伪代码
//子view重写dispatchTouchEvent
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN -> {
parent.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_MOVE -> {
if (父容器需要此类点击事件) {
parent.requestDisallowInterceptTouchEvent(false)
}
}
MotionEvent.ACTION_UP -> {
}
else -> {
}
}
return super.dispatchTouchEvent(ev)
}
//父view重写onInterceptTouchEvent
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
val action: Int = ev!!.action
return action != MotionEvent.ACTION_DOWN
}a、内部拦截法要求父View不能拦截ACTION_DOWN事件,由于ACTION_DOWN不受FLAG_DISALLOW_INTERCEPT标志位控制,一旦父容器拦截ACTION_DOWN那么所有的事件都不会传递给子View b、滑动策略的逻辑放在子View的dispatchTouchEvent方法的ACTION_MOVE中,如果父容器需要获取点击事件则调用 parent.requestDisallowInterceptTouchEvent(false)方法,让父容器去拦截事件。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。