1. 程式人生 > >Android 事件傳遞與焦點處理(tv)

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 AMSActivityThread處理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事件分發機制二:viewGroupview對事件處理

## 前言 很高興遇見你~ 在上一篇文章 [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 是