理解Android View的事件传递机制

在android事件传递一般包括三个对象: Activity,ViewGroup,View,事件分发顺序为:Activity->ViewGroup->View,事件分发过程由

onTouchEvent()
onInterceptTouchEvent()
dispatchTouchEvent()

这三个方法协助完成, 其中View没有onInterceptTouchEvent()方法

下面用一个简单的例子进行说明:

testButton, testLayout为自定义控件

class MainActivity : AppCompatActivity(), View.OnTouchListener {
 override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//view没有onInterceptTouchEvent
//onTouch事件优于onClick
btnTest.setOnTouchListener(this)
btnTest.setOnClickListener {
Toast.makeText(this@MainActivity,"te", Toast.LENGTH_LONG).show()
}
}

override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (v){
mainLayout->{
when (event?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","mainLayout ACTION_DOWN")
}
MotionEvent.ACTION_UP->{
Log.d("touch event","mainLayout ACTION_UP")
}
}
}
btnTest->{
when (event?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","btnTest ACTION_DOWN")
}
MotionEvent.ACTION_UP->{
Log.d("touch event","btnTest ACTION_UP")
}
}
}
}
return true
}

//activity的dispatchTouchEvent
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
//事件分派
when (ev?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","activity dispatchTouchEvent ACTION_DOWN")
//在ACTION_DOWN return true,子view收不到后面的事件
//return true
}
MotionEvent.ACTION_UP->{
Log.d("touch event","activity dispatchTouchEvent ACTION_UP")
//ACTION_UP return true,子view收不到ACTION_UP
//return true
}
}
return super.dispatchTouchEvent(ev)
}

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

}
class TestButton:Button {

    constructor(mContext: Context) : super(mContext) {
        val context = mContext
    }
    constructor(mContext: Context, mAttributeSet: AttributeSet) : super(mContext, mAttributeSet) {
        val context = mContext
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        //return super.onTouchEvent(event)
        when (event?.action){
            MotionEvent.ACTION_DOWN->{
                Log.d("touch event","btnTest ACTION_DOWN")
                return true
            }
            MotionEvent.ACTION_UP->{
                Log.d("touch event","btnTest ACTION_UP")
            }
        }
        //如果返回true,不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用
          return true
    }

    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        return super.dispatchTouchEvent(event)
    }

}

  

class TestLayout : ConstraintLayout{

    constructor(mContext: Context) : super(mContext) {
        val context = mContext
    }
    constructor(mContext: Context, mAttributeSet: AttributeSet) : super(mContext, mAttributeSet) {
        val context = mContext
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        //return super.onTouchEvent(event)
        Log.d("touch event","view group")
        return true
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        //return super.onInterceptTouchEvent(ev)
        //true:中断事件
        //中断后, TestLayout的子view btnTest的onTouch,onClick都不会执行
        return true
    }

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        //事件分派
        when (ev?.action){
            MotionEvent.ACTION_DOWN->{
                Log.d("touch event","view group dispatchTouchEvent ACTION_DOWN")
                //在ACTION_DOWN return true,子view收不到后面的事件
                //return true
            }
            MotionEvent.ACTION_UP->{
                Log.d("touch event","view group dispatchTouchEvent ACTION_UP")
                //ACTION_UP return true,子view收不到ACTION_UP
                //return true
            }
        }
        return super.dispatchTouchEvent(ev)
    }
}

 

我们通过log查看到,每次touch btnTest或mainLayout,都会先打印Activity里的dispatchTouchEvent里的ACTION_DOWN,是传递的起点

a. 如果Activity的dispatchTouchEvent里的ACTION_DOWN return true,子view就收不到事件传递了;

b. TestLayout的onInterceptTouchEvent,如果return true,表示中断传递给子view的事件,TestButton的onTouch,onClick都不会执行,会走TestLayout的onTouchEvent;就是说如果ViewGroup的onInterceptTouchEvent return true,就会走自已的onTouchEvent();

c. 如果TestLayout的onTouchEvent return true,就是消费掉事件,不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用

d. 同理TestButton的onTouchEvent return true,也不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用

 

如果view,viewgroup都没有对事件进行消费,会以activity的onTouchEvent作为终点;

如果viewgroup中断事件并对事件进行消费,会以viewgroup onTouchEvent作为终点;

如果viewgroup没有中断事件,但view(如button)对事件进行消费,会以button ACTION_UP作为终点;

如果viewgroup没有中断事件,view也没有对事件进行消费,会以activity的onTouchEvent作为终点;



原文地址:https://www.cnblogs.com/johnnyzhao/p/10350809.html