android搶焦點
❶ Android TV開發焦點移動源碼分析
點可以理解為選中態,在Android TV上起很重要的作用。一個視圖控制項只有在獲得焦點的狀態下,才能響應按鍵的Click事件。
相對於手機上用手指點擊屏幕產生的Click事件, 在TV中通過點擊遙控器的方向鍵來控制焦點的移動。當焦點移動到目標控制項上之後,按下遙控器的確定鍵,才會觸發一個Click事件,進而去做下一步的處理
在處理焦點的時候,有一些基礎的用法需要知道。
首先,一個控制項isFocusable()需要為true才有資格可以獲取到焦點。如果想要在觸摸模式下獲取焦點,需要通過setFocusableInTouchMode(boolean)來設置。也可以直接在xml布局文件中指定:
keyEvent 分發過程:
而當按下遙控器的按鍵時,會產生一個按鍵事件,就是KeyEvent,包含「上」,「下」,「左」,「右」,「返回」,「確定」等指令。焦點的處理就在KeyEvent的分發當中完成。
首先,KeyEvent會流轉到ViewRootImpl中開始進行處理,具體方法是內部類 ViewPostImeInputStage 中的 processKeyEvent :
接下來先看一下KeyEvent在view框架中的分發:
這里也是可以做焦點控制,最好是在 event.getAction() == KeyEvent.ACTION_DOWN 進行.
因為android 的 ViewRootlmpl 的 processKeyEvent 焦點搜索與請求的地方 進行了判斷if (event.getAction() == KeyEvent.ACTION_DOWN)
• 首先ViewGroup會一層一層往上執行父類的dispatchKeyEvent方法,如果返回true那麼父類的dispatchKeyEvent方法就會返回true,也就代表父類消費了該焦點事件,那麼焦點事件自然就不會往下進行分發。
• 然後ViewGroup會悔李鍵判斷mFocused這個view是否為空,如果為空就會return false,焦點繼續往下傳遞;如果不為空,那就會return mFocused的dispatchKeyEvent方法返回的結果。這個mFocused其實是擾殲ViewGroup中當前獲取焦點的子View
發現執行了onKeyListener中的onKey方法,如果onKey方法返回true,那麼dispatchKeyEvent方法也會返回true
如果想要修改ViewGroup焦點事件的分發
• 重寫view的dispatchKeyEvent方法
• 給某個子view設置onKeyListener監聽
下面再來看一下如果一個頁面第一次進入,系統是如何確定焦點是定位在哪個view上的
由於DecorView繼承自FrameLayout,這里調用的是ViewGroup的requestFocus
descendantFocusability:
• FOCUS_AFTER_DESCENDANTS:先分發給Child View進行處理,如果所有的Child View都沒有處理,則自己再處理
• FOCUS_BEFORE_DESCENDANTS:ViewGroup先對焦碧巧點進行處理,如果沒有處理則分發給child View進行處理
• FOCUS_BLOCK_DESCENDANTS:ViewGroup本身進行處理,不管是否處理成功,都不會分發給ChildView進行處理
因為 PhoneWindow 給 DecoreView 初始化時設置 了 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS),所以這里默認是FOCUS_AFTER_DESCENDANTS
到此第一次請求焦點的過程基本告一個段落
焦點移動的時候,默認的情況下,會按照一種演算法去找在指定移動方向上最近的鄰居。在一些情況下,焦點的移動可能跟開發者的意圖不符,這時開發者可以在布局文件中使用下面這些XML屬性來指定下一個焦點對象:
在KeyEvent分發中已經知道如果分發過程中event沒有被消耗,就會根據方向搜索以及請求焦點View
流程一:查找用戶指定的下一個焦點
流程二:獲取搜索方向上所有可以獲取焦點的view,使用演算法查找下一個view
addFocusables() 獲取搜索方向上可獲得焦點的view
descendantFocusability屬性決定了ViewGroup和其子view的聚焦優先順序
• FOCUS_BLOCK_DESCENDANTS:viewgroup會覆蓋子類控制項而直接獲得焦點
• FOCUS_BEFORE_DESCENDANTS:viewgroup會覆蓋子類控制項而直接獲得焦點
• FOCUS_AFTER_DESCENDANTS:viewgroup只有當其子類控制項不需要獲取焦點時才獲取焦點
addFocusables 的第一個參數views是由root決定的。在ViewGroup的focusSearch方法中傳進來的root是DecorView,也可以主動調用FocusFinder的findNextFocus方法,在指定的ViewGroup中查找焦點。
FocusFinder.findNextFocus 查找焦點
❷ android tv常見問題(一)焦點查找規律
https://github.com/Geekholt/TvFocus
Recyclerview聚焦到最後一個Item,繼續按下鍵,焦點保持不變。
Recyclerview聚焦到最後一個Item,繼續按下鍵,焦點會跳出RecyclerView,跳到附近的View上。
那麼當Recyclerview滑動到最底部時,按下鍵,Android系統是如何找到下一個需要被聚焦的view的呢?我們把斷點打在ViewGroup的focusSearch方法上,可以看到從ViewRootImp的performFocusNavigation方法開始,依次調用了如下方法。
View並不會直接去找焦點,而是交給它的parent去找。
焦點會逐級的交給父ViewGroup的focusSearch方法去處理,直到最外層的布局,最後實際上是調用了FocusFinder的findNextFocus方法去尋找新的焦點。
但是這里要注意的是,RecyclerView和其他的ViewGroup不一樣,它自己重寫了focusSearch方法。所以在焦點查找委託到達到DecorView之前,會先執行RecyclerView的focusSearch方法。
那麼,RecyclerView和其他ViewGroup在尋找焦點方面有什麼不一樣呢? 為什麼RecyclerView要重寫ViewGroup的焦點查找機制呢 ?想知道這些問題的答案,那我們首先要知道ViewGroup的焦點查找機制。
ViewGroup的焦點查找機制的核心其實就是FocusFinder的findNextFocus方法。
主要步驟:
主要注意三點:
在addFocusables之後,找到指定方向上與當前focused距離最近的view。在進行查找之前,會統一坐標系。
總的來說就是根據當前focused的位置以及按鍵的方向,循環比較focusable集合中哪一個最適合,然後返回最合適的view,焦點查找就算完成了。
用於比較的方法。分別是將 當前聚焦的view , 當前遍歷到的focusable 和 目前為止最合適的focusable (i = 0時是優先順序最低的rect)進行比較。
判斷是否可以做為候選。可以看作是一個初步篩選的方法,但是到底哪個更好還需要看beamBeat方法,這個方法會將通過篩選的focusable和當前最合適的focusable進行比較,選出更合適的一個。
到這里為止ViewGroup的focusSearch方法基本上就講完了。那麼下面來看一下RecyclerView的focusSearch方法是如何實現焦點查找的。
前面講到了,該方法主要是為了解決 RecyclerView聚焦在按鍵方向上、當前屏幕區域內可見的最後一個item時,當前不可見的下一個item將無法獲得焦點。
這個方法是由LayoutManager來實現的,這就是RecyclerView的針對上面提到的情況的焦點查找方法。這里主要分析LinearLayoutManager中實現的該方法,如果在使用其他的LayoutManager時出現RecyclelerView焦點不符合預期的話,可以查看對於LayoutManager下的onFocusSearchFailed方法。
主要關注方法,通過這個方法的命名我們大致就可以看出來這個方法的作用了。這個方法主要會 根據當前RecyclerVIew的正逆序以及按鍵方向,找出最近一個部分或完全不可見的View 。
這個方法是RecyclerView內部的方法,和FocusFinder中的isCandidate方法的邏輯可以說幾乎是一摸一樣的。
到此為止ViewGroup的focusSearch和RecyclerVIew的focusSearch都分析完了。我們已經知道RecyclerView滑動到最底部的時候,發生了哪些焦點行為,那麼解決起來就比較簡單了。
結合KeyEvent事件的流轉,處理焦點的時機,按照優先順序(順序)依次是:
以上任一處都可以指定焦點,一旦消費了就不再往下走。
比如前面說到了RecyclerView就是通過重寫focusSearch方法對邊界上部分可見或不可見的view的焦點查找進行了特殊處理。
重寫RecyclerView的focusSearch方法
❸ Android 輸入法窗口焦點獲取流程(2) ,輸入法窗口和應用窗口綁定
基於Android9.x
Window和Session創建成功後,窗口的下一步流程為獲取焦點
我們看下焦點獲取過程,跟輸入法相關的流程
兩個Activity切換時,對應的狀態變化過程為:
以下是Activity窗口初次獲取焦點的流程
當兩個activity 切換時,失去焦點的窗口調用過程如下:
對應的,獲取焦點的額窗口的調用過程如下:
當B窗口的狀態切換到RESUMED時,當窗口的focus可能變化時,會調用updateFocusedWindowLocked
在該方法中,判斷,如果還沒有執行startInputInner方法,則執行startInputInner方法,否則,直接執行startInputOrWindowGainedFocus方法
主要流程:
1:設置controlFlags的flag為CONTROL_WINDOW_FIRST
2:檢查是否已經執行過startInputInner,沒有的話執行startInputInner-->startInputOrWindowGainedFocus;否則,直接執行startInputOrWindowGainedFocus
兩條路徑,攜帶的startInputReason參數不一樣
主要流程:
1:檢查要啟動和退出的ServedView是否為同一個,如果為同一個,則表示已經執行過startInputInner,則返回false,表示不再執行startInputInner
2:如果獲取焦點的是EditorText,會創建跟IMS通信的mServedInputConnectionWrapper對象
主要流程:
1:創建EditorInfo對象tba,這個參數對TextView布局才有意義,它的初始化是在mServedView的onCreateInputConnection完成實例化的
2:根據EditorInfo創建一個InputConnection對象,輸入法應用通過該對象,完成輸入內容到輸入框的傳遞;ACTIVITY獲取焦點場景,該對象
為null,因為沒有要輸入的對象
startInputOrWindowGainedFocus攜帶的參數
startInputReason = 1
表示,該流程是窗口獲取焦點過程
mClient
應用層創建的IInputMethodClient對象,為服務層提供應用層的各個回調方法
該方法跟應用進程首次創建時Session時,傳遞到IMMS的對象是同一個對象
windowGainingFocus:
應用層的ViewRootImpl$W對象
controlFlags |= CONTROL_START_INITIAL;
表示window窗口剛開始獲取焦點
softInputMode = SOFT_INPUT_ADJUST_RESIZE , 允許調整輸入法窗口,避免被其他窗口遮擋
tba , EditorInfo對象
servedContext
null
missingMethodFlags
ic等於null的情況下,為0
當應用層傳遞的W對象windowToken不為null的時候,則創建windowGainedFocus對象,返回給app
結果返回後,會對IMM的對象進行賦值
如此,進入一個窗口,獲取窗口焦點過程,窗口與輸入法相關的流程,就結束了。
下一篇:輸入法在輸入框彈出流程
Android輸入法(3),彈出流程
❹ Android 獲取焦點
你好,我舉例說明:比如有個輸入框,當你點擊輸入框時,這時它的游標在閃爍,可以輸入文字,那麼就可以說這個輸入框獲得焦點了。獲取焦點的控制項就是當前可以執行操作的控制項。
使用view.requestFocus()方法可以手動獲取焦點。
以上,希望對你有幫助。
❺ Android EditText獲取焦點並彈出軟鍵盤
1、首先,在xml文件中通過讓edittext獲取焦點
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="17dp"
android:textColor="#333333"
android:textColorHint="#999999"
android:hint="請輸入課件描述..."
android:gravity="top|left"
android:background="@null"
>
<requestFocus/>
</EditText>
2、在清單文件中給activity添加android:windowSoftInputMode=」stateVisible」屬性,這樣一進入這個頁面的時候游標就自動顯示,軟鍵盤也顯示出來
<activity
android:name=".wonderfulmoment."
android:windowSoftInputMode="stateVisible"></activity>