Android-兩層view疊加帶來的響應問題
前幾天在公司遇到一個雙層view疊加,而此時系統存在click聲音導致點選上層view空白處有聲音的問題。
雙層view疊加不同於單個view的觸控事件分發機制,單個view的觸控事件
我們先看對於一個viewGroup來說,觸控事件的分發
一般來說,開發Android應用程式的UI介面都不會直接實用View和ViewGroup,而是使用這兩大基類的派生類。
Android 中與 Touch 事件相關的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);
###### 1.事件分發:(看過我上篇Fragment中監聽觸控事件的兄弟就該知道該方法的妙用)
public boolean dispatchTouchEvent(MotionEvent ev)
該方法會以隧道方式(從根元素依次往下傳遞直到最內層子元素或在中間某一元素中由於某一條件停止傳遞)將事件傳遞給最外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法,並由該 View 的 dispatchTouchEvent(MotionEvent ev)方法對事件進行分發
該方法的處理
return true,事件會分發給當前 View 並由 dispatchTouchEvent方法進行消費,同時事件會停止向下傳遞;
return false,事件分發分為三種情況:
如果當前 View 獲取的事件直接來自 Activity,則會將事件返回給 Activity 的 onTouchEvent 進行消費
如果當前 View 獲取的事件來自外層父控制元件,則會將事件返回給父 View 的onTouchEvent 進行消費
如果返回系統預設的 super.dispatchTouchEvent(ev),事件會自動的分發給當前 View 的 onInterceptTouchEvent 方法
##### 2.事件攔截:public boolean onInterceptTouchEvent(MotionEvent ev)
在外層 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系統預設的 super.dispatchTouchEvent(ev) 情況下,事件會自動的分發給當前 View 的 onInterceptTouchEvent 方法。
該方法的處理邏輯:
- return true,則表示將事件進行攔截,並將攔截到的事件交由當前 View 的 onTouchEvent 進行處理;
- return false,則表示將事件放行,當前 View 上的事件會被傳遞到子 View 上,再由子 View 的dispatchTouchEvent 來開始這個事件的分發;
- 如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件預設會被攔截,並將攔截到的事件交由當前 View 的 onTouchEvent 進行處理。
3.事件響應:public boolean onTouchEvent(MotionEvent ev)
在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 並且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情況下 onTouchEvent 會被呼叫。
onTouchEvent 的事件響應邏輯如下:
- return false,那麼這個事件會從當前 View 向上傳遞,並且都是由上層 View 的 onTouchEvent 來接收,如果傳遞到上面的 onTouchEvent 也返回 false,這個事件就會“消失”,而且接收不到下一次事件。
- return true 則會接收並消費該事件。
- return super.onTouchEvent(ev) 預設處理事件的邏輯和返回 false 時相同。
那麼,我們的觸控事件分發圖也就誕生了。
解決問題
理解了觸控事件的分發後,我們面臨的問題是,疊加後的底面那層響應了點選事件,且發出了點選聲音。而我們底面的view並不是上層view的子view,其是類似於frame疊加的效果。
那麼我們可以分析出,是我們點選上層view空白處,touch事件沒有被消費的問題,在上層空白處,我們的觸控事件被分發到了下一層,通過==uiautomatorviewer==進行UI分析發現存在view疊層的問題,此時問題解決方法就出來了,我們只需在上層view,觸控事件沒被處理的情況下自己主動return ture;
告訴系統我們響應了觸控事件了即可,此時觸控事件便不會被系統分發給下一層的view
後來發現setting中很多地方存在該問題,好在那些view都是繼承的LinerLayout於是我寫了個TouchEventConsumerLayout父類,讓他們都繼承TouchEventConsumerLayout,問題得到了解決。且子view
click事件都能正常發生。
畢竟,clicklistener是優先處理的。且子view的觸控事件也是優先處理的。
好了,讓我們看一下該類。
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class TouchEventConsumerLayout extends LinearLayout{
public TouchEventConsumerLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public TouchEventConsumerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchEventConsumerLayout(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}
###謝謝大家閱讀,如有幫助,來個喜歡或者關注吧!
本文作者:Anderson/Jerey_Jobs