一般的触摸事件传递顺序是ViewGroupRoot -> ViewGroupA -> View这样的。现在如果ViewGroupRoot下有两个同级的ViewGroupA和ViewGroupB,且两者宽高均为match_parent,那么请问这时的触摸事件传递顺序是怎样的?ViewGroupA和ViewGroupB谁先收到触摸事件?
怪我咯2017-04-17 17:43:57
打log或debug只是知道結果卻不知道為何, 我簡單分析下決定這個傳遞順序的邏輯
觸摸事件的傳遞順序的邏輯應該在父控件的dispatchTouchEvent()
方法中, 不同的父控件這個方法有可能不一樣, 如果父控件的這個方法的實現改變了, 有可能會改變這個順序.不過一般不會改的, 所以我們只看ViewGroup
的實現,看原始碼, 很容易找到把觸控事件傳遞給子控制項的那部分程式碼, 基本變數命名出現child那堆就是了, 關鍵的邏輯應該是下面幾行dispatchTouchEvent()
方法中, 不同的父控件这个方法有可能不一样, 如果父控件的这个方法的实现改变了, 有可能会改变这个顺序.不过一般不会改的, 所以我们只看ViewGroup
的实现, 看源码, 很容易找到把触摸事件传递给子控件的那部分代码, 基本变量命名出现child那堆就是了, 关键的逻辑应该是下面几行
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
// 省略部分代码
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex);
// 省略一堆后续判断
}
}
这里可以明确看到定义了一个变量final View child
, 这个就是事件将要传递的子控件, 当然后面还要经过一些判断才会把事件分发给它, 不过这里不关心了.
决定这个child
的实例的逻辑是final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex);
,
关键的参数有两个, preorderedList
和childIndex
, 到这里基本可以确定触摸事件的分发顺序就是这两个参数决定的了.
接着看preorderedList
, 是调用了方法buildOrderedChildList()
, 这个方法的代码就不贴出来了, 如果你没有对子控件设置elevation或者translationZ, 那么就会返回空, 如果设置了的话那么返回一个根据Z轴排序的列表, 一般情况下都是没有设置的, 如果你设置了Z轴的值, 那么在Z轴的值越大就越优先分发事件.
然后看childIndex
, 一般情况下这个值就是xml文件中定义的顺序了, 不过我们可以通过方法getChildDrawingOrder()
和setChildrenDrawingOrderEnabled(boolean enabled)
来自定义子控件的绘制顺序, 如果你设置setChildrenDrawingOrderEnabled(true)
那么isChildrenDrawingOrderEnabled()
就会返回true
, 导致customOrder
变量在preorderedList
为null的情况下是true
, 接着就会调用getChildDrawingOrder()
方法来获取当前事件分发的子控件的index.
总结, 你点击的区域有两个View, A和B, 它们大小相同, 位置重合
如果你对A或B设置了elevation或者translationZ, 那么会先分发给Z轴上值较大的View, 不设置的View默认是0, 此时index只能是xml上添加的顺序
如果你没有设置Z轴的值, 设置了setChildrenDrawingOrderEnabled(true)
和实现了父控件的getChildDrawingOrder()
rrreee
final View child
, 這個就是事件將要傳遞的子控制項, 當然後面還要經過一些判斷才會把事件分發給它, 不過這裡不關心了.🎜
🎜決定這個child
的實例的邏輯是final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex);
,🎜關鍵的參數有兩個, preorderedList
和childIndex
, 到這裡基本上可以確定觸摸事件的分發順序就是這兩個參數決定的了.🎜
🎜接著看preorderedList
, 是呼叫了方法buildOrderedChildList()
, 這個方法的程式碼就不貼出來了, 如果你沒有對子控制設定elevation或translationZ, 那麼就會回傳空, 如果設定了的話那麼回傳一個根據Z軸排序的列表, 一般情況下都是沒有設定的, 如果你設定了Z軸的值, 那麼在Z軸的值越大就越優先分發事件.🎜
🎜然後看childIndex
, 一般情況下這個值就是xml檔案中定義的順序了, 不過我們可以透過方法getChildDrawingOrder()
和setChildrenDrawingOrderEnabled(boolean enabled)
來自訂子控制項的繪製順序, 如果你設定setChildrenDrawingOrderEnabled(true)
那麼isChildrenDrawingOrderEnabled()
就會回傳true
, 導致customOrder
變數在preorderedList
為null的情況下是true
, 接著就會呼叫getChildDrawingOrder()
方法來取得目前事件分發的子控制項的index.🎜
🎜總結, 你點擊的區域有兩個View, A和B, 它們大小相同, 位置重合🎜
setChildrenDrawingOrderEnabled(true)
和實作了父控制項的getChildDrawingOrder()
方法, 那麼順序就是由這個方法裡的實現確定了, 例如在這個方法傳入參數是0的時候返回的是A的index, 傳入1的時候返回的是B的index, 即使實際上A的index比B大, 那麼事件也會先傳遞給A🎜如果你什麼都沒幹, 就是正常使用, 那麼分發順序就是子控件在xml中的順序的倒序, 就是後添加的先分發, 實際上如果兩個控件重合了, 你看到的也是後來新增的控制項, 那麼自然點擊事件也是先分發給後新增的控制項了
大致是這樣, 有錯歡迎指正.