導航:首頁 > 操作系統 > android點擊事件分發

android點擊事件分發

發布時間:2022-06-10 09:13:52

android 如何獲取一個界面最頂層的view並處理單擊事件的分發機制

android事件分發機制 就是一個觸摸事件發生了,從一個窗口傳遞到一個視圖,再傳遞到另外一個視圖,最後被消費的過程,在android中還是比較復雜的傳遞流程如下:

(1) 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對事件進行處理。

(2) 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。

㈡ Android的handler機制的原理

Android的handler機制的原理分為非同步通信准備,消息發送,消息循環,消息處理。

1、非同步通信准備

在主線程中創建處理器對象(Looper)、消息隊列對象(Message Queue)和Handler對象。

2、消息入隊

工作線程通過Handler發送消息(Message) 到消息隊列(Message Queue)中。

3、消息循環

消息出隊: Looper循環取出消息隊列(Message Queue) 中的的消息(Message)。

消息分發: Looper將取出的消息 (Message) 發送給創建該消息的處理者(Handler)。

4、消息處理

處理者(Handler) 接收處理器(Looper) 發送過來的消息(Message),根據消息(Message) 進行U操作。

handler的作用

handler是android線程之間的消息機制,主要的作用是將一個任務切換到指定的線程中去執行,(准確的說是切換到構成handler的looper所在的線程中去出處理)android系統中的一個例子就是主線程中的所有操作都是通過主線程中的handler去處理的。

Handler的運行需要底層的 messagequeue和 looper做支撐。



㈢ android activity怎樣分發事件

現在讓我們創建一個簡單的Activity,創建一個TestLinearLayout繼承自LinearLayout,創建一個Test繼承自Button。
在TestLineaLayout類中,重寫了和事件相關的代碼,整個代碼如下:
public class TestLinearLayout extends LinearLayout {

public TestLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("TestLinerLayout", "onInterceptTouchEvent action = " + ev.getAction());
return super.onInterceptTouchEvent(ev);

㈣ android 事件分發方法怎麼走

當一個Touch事件(觸摸事件為例)到達根節點,即Acitivty的ViewGroup時,它會依次下發,下發的過程是調用子View(ViewGroup)的dispatchTouchEvent方法實現的。簡單來說,就是ViewGroup遍歷它包含著的子View,調用每個View的dispatchTouchEvent方法,而當子View為ViewGroup時,又會通過調用ViwGroup的dispatchTouchEvent方法繼續調用其內部的View的dispatchTouchEvent方法。上述例子中的消息下發順序是這樣的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只負責事件的分發,它擁有boolean類型的返回值,當返回為true時,順序下發會中斷。在上述例子中如果⑤的dispatchTouchEvent返回結果為true,那麼⑥-⑦-③-④將都接收不到本次Touch事件。來個簡單版的代碼加深理解:

/**
* ViewGroup
* @param ev
* @return
*/
public boolean dispatchTouchEvent(MotionEvent ev){
....//其他處理,在此不管
View[] views=getChildView();
for(int i=0;i<views.length;i++){
//判斷下Touch到屏幕上的點在該子View上面
if(...){
if(views[i].dispatchTouchEvent(ev))
return true;
}
}
...//其他處理,在此不管
}
/**
* View
* @param ev
* @return
*/
public boolean dispatchTouchEvent(MotionEvent ev){
....//其他處理,在此不管
return false;
}

㈤ 誰可以解釋下,android事件分發為什麼要設計成從根view到子view,而不是從子vie

Android事件傳遞流程在網上可以找到很多資料,FrameWork層輸入事件和消費事件,可以參考:
Touch事件派發過程詳解
這篇blog闡述了底層是如何處理屏幕輸,並往上傳遞的。Touch事件傳遞到Activity的DecorView時,往下走就是ViewGroup和子View之間的事件傳遞,可以參考郭神的這兩篇博客
Android事件分發機制完全解析,帶你從源碼的角度徹底理解(上)
Android事件分發機制完全解析,帶你從源碼的角度徹底理解(下)
郭神的兩篇博客清楚明白地說明了View之間事件傳遞的大方向,但是具體的一些晦暗的細節闡述較少,本文主要是總結這兩篇博客的同時,側重於兩點:
事件分發過程中一些細節到底如何實現的?
子view到底如何和父View搶事件,父View又是如何攔截事件不發送給子View,以及如果我們需要處理這種混亂的關系才能讓兩者和諧相處?。
MotionEvent抽象
要明白View的事件傳遞,很有必要先說一下Touch事件是如何在Android系統中抽象的,這主要使用的就是MotionEvent。這個類經歷了幾次重大的修改,一次是在2.x版本支持多點觸摸,一次是4.x將大部分代碼甩給native層處理。
一次簡單的事件
我們先舉個栗子來說明一次完整的事件,用戶觸屏 滑動 到手機離開屏幕,這認為是一次完整動作序列(movement traces)。一個動作序列中包含很多動作Action,比如在用戶按下時,會封裝一個MotionEvent,分發給視圖樹,我們可以通過motionevent.getAction拿到這個動作是ACTION_DOWN。同樣,在手指抬起時,我們可以接收到Action類型是Action_UP的MotionEvent。對於滑動(MOVE)這個操作,Android為了從效率出發,會將多個MOVE動作打包到一個MotionEvent中。通過getX getY可以獲取當前的坐標,如果要訪問打包的緩存數據,可以通過getHistorical**()函數來獲取。
加入多點觸摸
對於單點的操作來看,MotionEvent顯得比較簡單,但是考慮引入多點觸摸呢?我們定義一個接觸點為(Pointer)。每一個觸摸點Pointer都會有一個當次動作序列的唯一Id和Index.MotionEvent中多個手指的操作API大部分都是通過pointerindex來進行的,如:獲取不同Pointer的觸碰位置,getX(int pointerIndex);獲取PointerId等等。大部分情況下,pointerid == pointeridex。
我們從onTouch接受到一個MotionEvent,怎麼拿到多個觸碰點的信息?為了解開筆者剛開始學習這部分知識時的困惑,我們首先樹立起一種概念:一個MotionEvent只允許有一個Action(動作),而且這個Action會包含觸發這次Action的觸碰點信息,對於MOVE操作來說,一定是當前所有觸碰點都在動。只有ACTION_POINTER_DOWN這類事件事件會在Action裡面指定是哪一個POINTER按下。
在MotionEvent的底層實現中,是通過一個16位來存儲Action和Pointer信息(PointerIndex)。低8位表示Action,理論上可以表示255種動作類型;高8位表示觸發這個Action的PointerIndex,理論上Android最多可以支持255點同時觸摸,但是在上層代碼使用的時候,默認多點最多存在32個,不然事件在分發的時候會有問題。
ACTION_DOWN OR ACTION_POINTER_DOWN:
這兩個按下操作的區別是ACTION_DOWN是一個系列動作的開始,而ACTION_POINTER_DOWN是在一個系列動作中間有另外一個觸碰點觸碰到屏幕。
這部分詳細的描述,請參考:
android觸控,先了解MotionEvent
到這里,鋪墊終於結束了,我們開始直奔主題。
View的事件傳遞
Android的Touch事件傳遞到Activity頂層的DecorView(一個FrameLayout)之後,會通過ViewGroup一層層往視圖樹的上面傳遞,最終將事件傳遞給實際接收的View。下面給出一些重要的方法。如果你對這個流程比較熟悉的話,可以跳過這里,直接進入第二部分。
dispatchTouchEvent
事件傳遞到一個ViewGroup上面時,會調用dispatchTouchEvent。代碼有刪減
public boolean dispatchTouchEvent(MotionEvent ev) {

boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;

// Attention 1 :在按下時候清除一些狀態
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
//注意這個方法
resetTouchState();
}

// Attention 2:檢查是否需要攔截
final boolean intercepted;
//如果剛剛按下 或者 已經有子View來處理
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// 不是一個動作序列的開始 同時也沒有子View來處理,直接攔截
intercepted = true;
}

//事件沒有取消 同時沒有被當前ViewGroup攔截,去找是否有子View接盤
if (!canceled && !intercepted) {
//如果這是一系列動作的開始 或者有一個新的Pointer按下 我們需要去找能夠處理這個Pointer的子View
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down

//上面說的觸碰點32的限制就是這里導致
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);

//對當前ViewGroup的所有子View進行排序,在上層的放在開始
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);

// canViewReceivePointerEvents visible的View都可以接受事件
// isTransformedTouchPointInView 計算是否落在點擊區域上
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}

//能夠處理這個Pointer的View是否已經處理之前的Pointer,那麼把
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
} }
//Attention 3 : 直接發給子View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
= true;
break;
}

}
}

}
}

// 前面已經找到了接收事件的子View,如果為NULL,表示沒有子View來接手,當前ViewGroup需要來處理
if (mFirstTouchTarget == null) {
// ViewGroup處理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {

if() {
//ignore some code
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
}

}
return handled;
}

上面代碼中的Attention在後面部分將會涉及,重點注意。
這里需要指出一點的是,一系列動作中的不同Pointer可以分配給不同的View去響應。ViewGroup會維護一個PointerId和處理View的列表TouchTarget,一個TouchTarget代表一個可以處理Pointer的子View,當然一個View可以處理多個Pointer,比如兩根手指都在某一個子View區域。TouchTarget內部使用一個int來存儲它能處理的PointerId,一個int32位,這也就是上層為啥最多隻能允許同時最多32點觸碰。
看一下Attention 3 處的代碼,我們經常說view的dispatchTouchEvent如果返回false,那麼它就不能系列動作後面的動作,這是為啥呢?因為Attention 3處如果返回false,那麼它不會被記錄到TouchTarget中,ViewGroup認為你沒有能力處理這個事件。
這里可以看到,ViewGroup真正處理事件是在dispatchTransformedTouchEvent裡面,跟進去看看:
dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {

//沒有子類處理,那麼交給viewgroup處理
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}

handled = child.dispatchTouchEvent(transformedEvent);
}
return handled;
}

可以看到這里不管怎麼樣,都會調用View的dispatchTouchEvent,這是真正處理這一次點擊事件的地方。
dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
//先走View的onTouch事件,如果onTouch返回True
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}

我們給View設置的onTouch事件處在一個較高的優先順序,如果onTouch執行返回true,那麼就不會去走view的onTouchEvent,而我們一些點擊事件都是在onTouchEvent中處理的,這也是為什麼onTouch中返回true,view的點擊相關事件不會被處理。

㈥ android 事件分發中 為什麼down事件不消費 後續事件也得不到消費

事件都是從dispatchTouchEvent開始的, 如果down事件不消費, 那麼在dispatchTouchEvent源碼中的mFirstTouchTarget就是空的. mFirstTouchTarget這個變數是記錄消費down事件的. 沒有消費down 那麼mFirstTouchTarget就是空. 其他事件進入dispatchTouchEvent後 會先判斷mFirstTouchTarget為空, 就判定事件被攔截了. 就不會繼續事件分發了. 也不會去執行onTouchEvent方法了.
至於Android這樣設計的道理? 因為如果down事件不接受, 就默認其他事件也不要了. 這也是為了提高性能, 畢竟move是一大串的. 如果不在down中這樣設計. 每個move都去判斷, 勢必會影響觸摸響應性能.

㈦ android怎麼主動分發事件

Android中,所有的操作類型事件都由如下三個部分作為基礎:


按下(ACTION_DOWN)

移動(ACTION_MOVE)

抬起(ACTION_UP)

這三部分都寄生於onTouch事件中,由MontionEvent類中定義的三個常量進行區分。


Android中與Touch事件相關的方法為:

removeTapCallback();
removeLongPressCallback();
break;

case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();

// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();

setPressed(false);
}
}
break;
}
return true;
}

return false;
}

㈧ android事件分發中 怎麼確定點擊自己想點擊的界面

android監聽事件添加動作的三種方式:
第一種也是最開始就接觸的方式,通常在activity組件的oncreate事件中直接定義,直接動作。這種方式每個控制項都定義一次,通常不方便.
Button btn = (Button) findViewById(R.id.myButton);

btn .setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

//do something

}

});
第二種通常是在activity組件實現其介面,這樣可以多外控制項共享一個介面,這樣相對方便
public class TestMedia extends Activity implements View.OnClickListener{

Button btn1 = (Button) findViewById(R.id.myButton1);
Button btn2 = (Button) findViewById(R.id.myButton2);

btn1 .setOnClickListener();
btn2 .setOnClickListener();

}

public void onClick(View v) {

switch (v.getId()) {

case R.id. myButton1:

//do something

break;

case R.id. myButton2:

//do something

break;

}

第三種類似第二種,這樣的好處在於如果需要實現多個監聽介面,這樣更清晰

public class TestMedia extends Activity {

Button btn1 = (Button) findViewById(R.id.myButton1);
Button btn2 = (Button) findViewById(R.id.myButton2);

btn1 .setOnClickListener(new ClickEvent());

btn2 .setOnClickListener(new ClickEvent());

}
class ClickEvent implements View.OnClickListener

public void onClick(View v) {

switch (v.getId()) {

case R.id. myButton1:

//do something

break;

case R.id. myButton2:

//do something

break;

}

㈨ Android事件分發機制是怎麼判斷手勢類型,並決定分發給哪個view的

可以參考如下內容:
dispatchTouchEvent -> onTouch -> onTouchEvent
等上述的up事件分發完結後,再調用onClick
看起來一目瞭然,事實上,我只是用了默認的調用,在TextView的dispatchTouchEvent和onTouchEvent都是讓它返回super.xxx。而且在onTouch監聽里返回的是false。
所以,實際的情況更多,基於此,為了更清晰地熟悉事件分發機制,我們只能通過看源碼了。
源碼分析
我目前查看的源碼是API 22環境下的。這部分關於View的事件分發的源碼和之前的有很大的區別,但是,萬變不如其中,有些根本的邏輯流程一般是不會改變的。
我們直接看View的代碼,因為控制項都繼承自View。

㈩ android點擊事件分發機制中為什麼不能直接通過攔截來確定是否執行當前View的onTouch方法

我們根據攔截返回的布爾值判斷是否攔截,那麼,你該在哪裡寫if else呢,不還是得另寫一個方法判斷嗎,那不就是dispatchtouchevent

閱讀全文

與android點擊事件分發相關的資料

熱點內容
壓縮機製冷劑溫度 瀏覽:930
會日語的程序員 瀏覽:19
網銀密碼加密失敗怎麼回事 瀏覽:727
android開發音樂播放器 瀏覽:808
ug120陣列命令快捷鍵 瀏覽:597
氣動隔膜式壓縮機 瀏覽:470
linux如何修改主機名 瀏覽:104
單片機游標上下移動 瀏覽:528
數據加密驗證 瀏覽:108
程序員被激怒 瀏覽:891
winxp找不到伺服器dns地址 瀏覽:842
以文本文件的格式保存考生文件夾 瀏覽:41
編譯原理文法分為幾類 瀏覽:570
JAVA基礎學python要多久 瀏覽:74
java流量控制 瀏覽:936
java實現多重繼承 瀏覽:707
票據通加密狗怎麼在新系統上使用 瀏覽:795
航模加密狗連接電腦 瀏覽:473
好用的匯編語言編譯器 瀏覽:863
自己編譯安卓虛擬機 瀏覽:913