Android 事件傳遞與焦點處理(tv)
1、概述
上節介紹了android tv app 與android mobile app 的一些表現形式的不同。在實際程式設計中需要很多的焦點處理,而焦點處理有經常是在事件傳遞函式內處理的。所以本節做個android 事件傳遞與焦點處理的小結。另既然描述到android事件傳遞不可避免就涉及到了android手勢攔截。這也是對原有知識認識的一個補充,因為之前涉及到安卓事件傳遞就是為了做手勢攔截,以至於當看到程式碼在手勢分發函式裡處理tv的焦點,與介面移動填充時。一時有點迷糊,為什麼是寫在dispathKeyEvent裡不是寫在onInterceptTouchEvent裡。原因就是因為這是在事件傳遞流程裡處理焦點,而不是在事件傳遞流程裡攔截事件。
2、Android簡單的事件傳遞流程
提到android事件傳遞流程肯定會涉及到幾個以下幾個函式
函式 |
說明 |
備註 |
dispathTouchEvent (MotionEvent ev) |
事件分發(手勢分發) |
觸控式螢幕-手機 |
dispatchKeyEvent(KeyEvent event) |
事件分發 |
遙控- tv |
onInterceptTouchEvent |
事件攔截(手勢攔截) 返回true: 事件被攔截,事件傳遞給自己的 onTouchEvent 返回false:事件繼續傳遞 |
ViewGroup才有 |
onTouchEvent |
事件處理 返回true:事件被消費終止傳遞 返回false:繼續傳遞 |
表1
Android事件傳遞有兩種事件,一種是觸控式螢幕的觸控事件,一種就是按鍵事件,鍵盤模式和遙控就用這種,不在熬述。dispathTouchEvent 和 dispatchKeyEvent 走的流程是一樣的。
事件傳遞流程細節與原始碼的分析,網上有很多詳細的描述,有些說的也是雲裡霧裡,結合實際專案中的使用並參考:
PRE_andevcon_mastering-the-android-touch-system
事件傳遞從Activity的事件分發函式開始dispath… 如果沒有事件消費最終在回到activty的ontouchEvent在子view被消費了,那就不會傳到這個函式。
簡單的Down事件的傳遞如下圖和表1結合看:(事件未被消費)
圖1
圖2
關於攔截:對於攔截down事件後move/up 事件的傳遞比較複雜,實際使用中只要知道哪個view 是沒有down事件的也就沒有move/up 而且在實際專案中自己攔截的是move事件。onInterceptTouchEvent
public boolean onInterceptTouchEvent(MotionEvent ev)
事件攔截函式onInterceptTouchEvent是ViewGroup才有的函式。事件攔截(手勢攔截)也是在自定義ViewGroup時才要做的。實際情況遇到的是,對於move事件在一些邊界使用條件的時候要決定是讓ViewGroup自己處理還是childView來處理。其中通過返回值確定是否攔截,false繼續傳遞,true攔截給自己,流程見圖1,圖2。
3、dispathKeyEvent 與 dispathTouchEvent
事件分發函式,也是事件流程的排程函式。基類的dispath…函式決定了事件傳遞的方式,裡面有涉及到呼叫onInterceptTouchEvent,和onTouchEvent函式。
做自定ViewGroup事件攔截是不需要重寫dispath…的只要重寫onInterceptTouchEvent這個函式就可以。所以之前也比較少見重寫dispath…
但是在tv app裡好多地方是重寫了該函式。開始的時候有點不明白為什麼寫在這裡,後來看到每個dispathKeyEvent 函式都有呼叫super.dispathKeyEvent就明白了,重寫dispathKeyEvent並沒有影響事件的分發。只是相當於一個鉤子掛在事件傳遞流程上來處理:點選按鍵手勢時理焦點效果與介面效果,還有就是重寫dispathKeyEvent可以很方便的獲取到當前遙控的控制元件按鈕。而在onInterceptTouchEvent就不行。所以遙控裝置大量從寫dispathKeyEvent方法還是有道理的
4、焦點處理
手機app開發時焦點處理比較少,有就是editText有時候需要獲取下焦點,或者移除下,其實也比較少處理。Tv裡就比較多要處理,這裡羅列下幾個焦點處理函式
Activity |
|
View currentView = getCurrentFocus(); |
|
ViewGroup |
|
View currentView = findFocus(); |
|
View |
|
currentView.focusSearch(direction); |
找到指定方向最近的一個可獲取焦點的view 如果沒有返回null |
setClickedAble |
|
setFocusAble |
新增幾個函式:
((ViewGroup) parent).getFocusedChild() 獲取子佈局裡有焦點的view 沒有返回null。就不用自己去遍歷佈局了
public boolean hasFocus() 判斷view 是否有焦點 或者view的子view是否有焦點
Returns true if this view has focus iteself, or is the ancestor of the * view that has focus.
下面的明白的例子說明了幾個函式的具體意思
if (parent.hasFocus()) { if (parent.isFocused()) { return parent; } else { return getFocusView(((ViewGroup) parent).getFocusedChild()); } }
/** * Called when this view wants to give up focus. If focus is cleared * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called. * <p> * <strong>Note:</strong> When a View clears focus the framework is trying * to give focus to the first focusable View from the top. Hence, if this * View is the first from the top that can take focus, then all callbacks * related to clearing focus will be invoked after wich the framework will * give focus to this view. * </p> */ public void clearFocus() {
另外 api裡有 FocusFinder 工具類提供尋找focus
public View findNearestTouchable(ViewGroup root, int x, int y, int direction, int[] deltas)
public final View findNextFocus(ViewGroup root, View focused, int direction) {
public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
5、實際中遇到
一個需求:tabhost 點擊向上跳到set按鈕,setting按鈕點擊向下要回到上次的那個tabhost的select tab。如果不處理是跑到最近的一個tab
思路一:在setting按鈕的focus監聽器裡指定出焦點向下跑時跑到指定的id。
問題:tabhost對應的每個tab的id實際中都是一樣的,所以該思路廢棄
思路二:在setting的focus監聽器中指定當焦點丟失時,原來的tab亮起來
問題:無法再該監聽器中得到是上下左右那種焦點的丟失或者得到,該思路廢棄
思路三:setting按鈕屬於titleBar裡的一個view,那麼在titleBar這個繼承自layout的容器裡重寫dispathTouchEvent,該函式裡判斷是findfocus是在setting按鈕,並且操作是向下則把tabhost當前select的tab focus起來,並且攔截了該次的操作不繼續向下傳遞(繼續傳遞則預設最近的view會focus)
程式碼:
@Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { if (mSearchView.equals(findFocus())){ if (tabHost != null && tabHost.getCurrentTabView() != null){ tabHost.getCurrentTabView().requestFocus(); //return true 讓焦點跑到上次的地方,並且終止在該容器中手勢的繼續傳遞 return true; } } } return super.dispatchKeyEvent(event); }
6、有時候要找焦點。找焦點得先找子佈局
View view = mGridLayout.get(0).getAdapter().getView(0, null, null);
if (view != null){
if (view instanceof FrameLayout){
FrameLayout l = (FrameLayout) view;
//最外層的layout
LoggerUtil.d("MainFragment", "view = " + view);
int childCount = l.getChildCount();
//遍歷下面所有的子控制元件,判斷是否是layout
for(int i = 0; i < childCount; i++){
LoggerUtil.d("MainFragment", "view l = " + l.getChildAt(i));
if (l.getChildAt(i) instanceof VideoView){
LoggerUtil.d("MainFragment", "view lll = " + l.getChildAt(i));
l.getChildAt(i).requestFocus();
}
}
}
}
FOCUS_BEFORE_DESCENDANTS ViewGroup本身先對焦點進行處理,如果沒有處理則分發給child View進行處理
FOCUS_AFTER_DESCENDANTS 先分發給Child View進行處理,如果所有的Child View都沒有處理,則自己再處理
FOCUS_BLOCK_DESCENDANTS ViewGroup本身進行處理,不管是否處理成功,都不會分發給ChildView進行處理
相關推薦
Android 事件傳遞與焦點處理(tv)
1、概述 上節介紹了android tv app 與android mobile app 的一些表現形式的不同。在實際程式設計中需要很多的焦點處理,而焦點處理有經常是在事件傳遞函式內處理的。所以本節做個android 事件傳遞與焦點處理的小結。另既然描述到android事件
Android事件傳遞、多點觸控及滑動衝突的處理
基本概念 所有Touch事件都會被封裝MotionEvent, 包括Touch的型別、位置(相對螢幕的絕對位置,相對View的相對位置)、時間、歷史記錄以及第幾個手指(多點觸控)等; 事件有多種型別,常用的事件型別有:ACTION_DOWN,ACTION_UP,ACTION_MOVE,ACTION
Android 事件傳遞總結
Android 事件傳遞總結 首先 吐槽下17年的北京市場,簡直無法生存了,為了18年不被淘汰,只能不停的催眠自己,我愛學習,我愛鑽研,我更愛擼程式碼: 參考郭神的部落格 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解 View的事件分發處理過程
Android P AMS與ActivityThread處理Activity生命週期--ClientTransactionHandler
在Android P中,ActivityThread extends ClientTransactionHandler,而ClientTransactionHandler封裝了handlexxxActivity的方法。 而AMS控制Activity的生命週期,通過ApplicationThrea
(轉)QT事件傳遞與事件過濾器
Qt程式是事件驅動的, 程式的每個動作都是由幕後某個事件所觸發. Qt事件的型別很多, 常見的qt的事件如下: 鍵盤事件: 按鍵按下和鬆開. 滑鼠事件: 滑鼠移動,滑鼠按鍵的按下和鬆開. 拖放事件: 用滑鼠進行拖放. 滾輪事件: 滑鼠滾輪滾動. 繪屏事件: 重繪螢幕的某些部分. 定時事件: 定時器到時.
Android事件傳遞流程-虛擬碼
傳遞流程 Activity > ViewGroup > View Activity public boolean dispatchTouchEvent(MotionEvent ev) { //呼叫ViewGroup的方法d
細說Android事件傳遞機制(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent)
本文背景:前些天用到了之前寫的自定義圖片文字複合控制元件,在給他設定監聽時遇到了麻煩。雖然最後解決了問題,但發現在不重寫LinearLayout的onInterceptTouchEvent時,子ImageView、子TextView、父Linearlayout三者
Android事件傳遞機制
事件傳遞雖然算不上某個單獨的知識點,但是在實際專案開發中肯定會碰到,如果不明白其中的原理,那在設計各種滑動效果時就會感到很困惑。關於事件的傳遞,我們可能會有以下疑問:事件是如何傳遞的事件是如何處理的自定義view的時候,事件也衝突了怎麼解決帶著這三個疑問,我們來總結一下事件傳
Android事件傳遞機制詳解(巢狀自定義View示例)
一、概述 自定義View如果嵌套了自定義View,可能簡單寫一個onTouchEvent處理事件已經不能解決你的需要。簡單舉個例子: 你自定義了一個容器View,簡稱為父View,在這裡監聽點選事件,做事情A,監聽滑動做事情B 然後你又自定了一個View,放入該容器
QT父子視窗事件傳遞與事件過濾器
轉自:http://www.cnblogs.com/bingcaihuang/archive/2010/12/17/1909369.html 處理監控系統的時候遇到問題,在MainWidget中建立多個子Widget的時候,原意是想滑鼠點選先讓MainWidget截
Android 事件傳遞機制
有很多部落格寫過Android 事件傳遞機制,但是我看了一大圈,沒有讓我滿意的,或版本老舊,或過於複雜,或直接是有內在邏輯錯誤 參考內容: 《Android 開發藝術探索》 Android事件分發機制 詳解攻略,您值得擁有 本部落格只分析單指情況下,ACTION_DOWN,ACTION_MOVE,ACTI
android面試題之觸控事件分發與處理簡述
android觸控事件分發與處理 android的分發機制:由父控制元件判斷是否攔截,如果不攔截事件,則繼續分發到子控制元件,然後一直分發下去。 處理:與分發相反,由子控制元件先處理事件,如果子控制元件不處理,則交給父控制元件處理,一直向上傳遞,直到那個控制
android 觸控事件傳遞機制與筆記
一、筆記連結1. android 觸控事件傳遞機制2. android OnTouchListener,onTouchEvent,onClickListener執行順序 二、簡記1. android 觸控事件傳遞機制1.1Touch事件分發中只有兩個主角:ViewGroup和
Android開發知識(七):Android事件處理機制:事件分發、傳遞、攔截、處理機制的原理分析(上)
在我們剛開始學習安卓的時候,總會一開始就接觸到Button,也就是對按鈕進行一個事件監聽的事件,當我們點選螢幕上的按鈕時就可以觸發一個點選事件。那麼,從我們點選螢幕到按鈕觸發事件這個過程,是什麼樣子的呢?本文我們就來談一下關於事件攔截處理機制的基本知識。
Android開發知識(八):Android事件處理機制:事件分發、傳遞、攔截、處理機制的原理分析(中)
在本章節中,我們重點談論一下onTouch、onClick、onLongClick三個方法被回撥的過程。 在上一篇文章中,我們談到關於為View新增一個點選事件SetOnClickListener後,就可以通過回撥onClick方法來實現事件的響應
【Android TV】按鍵事件KeyEvent的分發處理流程解析
這次打算來梳理一下 Android Tv 中的按鍵點選事件 KeyEvent 的分發處理流程。一談到點選事件機制,網上資料已經非常齊全了,像什麼分發、攔截、處理三大流程啊;或者dispatchTouchEvent、onInterceptTouchEvent、
Android事件分發機制二:viewGroup與view對事件的處理
## 前言 很高興遇見你~ 在上一篇文章 [Android事件分發機制一:事件是如何到達activity的?](https://juejin.cn/post/6918272111152726024) 中,我們討論了觸控資訊從螢幕產生到傳送給具體 的view處理的整體流程,這裡先來簡單回顧一下: ![整體
Android筆記二十五.Android事件Handler消息傳遞機制
text table @override () lin dom align ttext 子線程 因為Android平臺不同意Activity新啟動的線程訪問該Activity裏的界面控件。這樣就會導致新啟動的線程無法動態改變界面控件的屬性值。但在實際Androi
Android 輸入管理服務-輸入事件到達之後的處理流程
content 例如 enter 技術 fontsize tail 流程 ref ora 接上一篇博客“Android 輸入管理服務啟動過程的流程”。這兩天分析了Android 輸入管理服務接收到輸入事件之後的處理流程,詳細流程例如以下面兩圖所看到的:
Android--Intent組件帶參傳遞與返回
puts 例子 ast pause onstop 圖標 訪問 數據頁面 .com Android 是 單例模式: 表示 application 唯一的。每個應用被啟動的時候,其實是 application 被創建。 Context 上下文: context 是