1、首先,在xml文件中通過讓edittext獲取焦點(diǎn)
月湖ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!
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”屬性,這樣一進(jìn)入這個(gè)頁面的時(shí)候光標(biāo)就自動(dòng)顯示,軟鍵盤也顯示出來
activity
? ? ? ? android:name=".wonderfulmoment.AddCourseWareDescriptionActivity"
? ? ? ? android:windowSoftInputMode="stateVisible"/activity
基于Android9.x
Window和Session創(chuàng)建成功后,窗口的下一步流程為獲取焦點(diǎn)
我們看下焦點(diǎn)獲取過程,跟輸入法相關(guān)的流程
兩個(gè)Activity切換時(shí),對應(yīng)的狀態(tài)變化過程為:
以下是Activity窗口初次獲取焦點(diǎn)的流程
當(dāng)兩個(gè)activity 切換時(shí),失去焦點(diǎn)的窗口調(diào)用過程如下:
對應(yīng)的,獲取焦點(diǎn)的額窗口的調(diào)用過程如下:
當(dāng)B窗口的狀態(tài)切換到RESUMED時(shí),當(dāng)窗口的focus可能變化時(shí),會(huì)調(diào)用updateFocusedWindowLocked
在該方法中,判斷,如果還沒有執(zhí)行startInputInner方法,則執(zhí)行startInputInner方法,否則,直接執(zhí)行startInputOrWindowGainedFocus方法
主要流程:
1:設(shè)置controlFlags的flag為CONTROL_WINDOW_FIRST
2:檢查是否已經(jīng)執(zhí)行過startInputInner,沒有的話執(zhí)行startInputInner--startInputOrWindowGainedFocus;否則,直接執(zhí)行startInputOrWindowGainedFocus
兩條路徑,攜帶的startInputReason參數(shù)不一樣
主要流程:
1:檢查要啟動(dòng)和退出的ServedView是否為同一個(gè),如果為同一個(gè),則表示已經(jīng)執(zhí)行過startInputInner,則返回false,表示不再執(zhí)行startInputInner
2:如果獲取焦點(diǎn)的是EditorText,會(huì)創(chuàng)建跟IMS通信的mServedInputConnectionWrapper對象
主要流程:
1:創(chuàng)建EditorInfo對象tba,這個(gè)參數(shù)對TextView布局才有意義,它的初始化是在mServedView的onCreateInputConnection完成實(shí)例化的
2:根據(jù)EditorInfo創(chuàng)建一個(gè)InputConnection對象,輸入法應(yīng)用通過該對象,完成輸入內(nèi)容到輸入框的傳遞;ACTIVITY獲取焦點(diǎn)場景,該對象
為null,因?yàn)闆]有要輸入的對象
startInputOrWindowGainedFocus攜帶的參數(shù)
startInputReason = 1
表示,該流程是窗口獲取焦點(diǎn)過程
mClient
應(yīng)用層創(chuàng)建的IInputMethodClient對象,為服務(wù)層提供應(yīng)用層的各個(gè)回調(diào)方法
該方法跟應(yīng)用進(jìn)程首次創(chuàng)建時(shí)Session時(shí),傳遞到IMMS的對象是同一個(gè)對象
windowGainingFocus:
應(yīng)用層的ViewRootImpl$W對象
controlFlags |= CONTROL_START_INITIAL;
表示window窗口剛開始獲取焦點(diǎn)
softInputMode = SOFT_INPUT_ADJUST_RESIZE , 允許調(diào)整輸入法窗口,避免被其他窗口遮擋
tba , EditorInfo對象
servedContext
null
missingMethodFlags
ic等于null的情況下,為0
當(dāng)應(yīng)用層傳遞的W對象windowToken不為null的時(shí)候,則創(chuàng)建windowGainedFocus對象,返回給app
結(jié)果返回后,會(huì)對IMM的對象進(jìn)行賦值
如此,進(jìn)入一個(gè)窗口,獲取窗口焦點(diǎn)過程,窗口與輸入法相關(guān)的流程,就結(jié)束了。
下一篇:輸入法在輸入框彈出流程
Android輸入法(3),彈出流程
Recyclerview聚焦到最后一個(gè)Item,繼續(xù)按下鍵,焦點(diǎn)保持不變。
Recyclerview聚焦到最后一個(gè)Item,繼續(xù)按下鍵,焦點(diǎn)會(huì)跳出RecyclerView,跳到附近的View上。
那么當(dāng)Recyclerview滑動(dòng)到最底部時(shí),按下鍵,Android系統(tǒng)是如何找到下一個(gè)需要被聚焦的view的呢?我們把斷點(diǎn)打在ViewGroup的focusSearch方法上,可以看到從ViewRootImp的performFocusNavigation方法開始,依次調(diào)用了如下方法。
View并不會(huì)直接去找焦點(diǎn),而是交給它的parent去找。
焦點(diǎn)會(huì)逐級(jí)的交給父ViewGroup的focusSearch方法去處理,直到最外層的布局,最后實(shí)際上是調(diào)用了FocusFinder的findNextFocus方法去尋找新的焦點(diǎn)。
但是這里要注意的是,RecyclerView和其他的ViewGroup不一樣,它自己重寫了focusSearch方法。所以在焦點(diǎn)查找委托到達(dá)到DecorView之前,會(huì)先執(zhí)行RecyclerView的focusSearch方法。
那么,RecyclerView和其他ViewGroup在尋找焦點(diǎn)方面有什么不一樣呢? 為什么RecyclerView要重寫ViewGroup的焦點(diǎn)查找機(jī)制呢 ?想知道這些問題的答案,那我們首先要知道ViewGroup的焦點(diǎn)查找機(jī)制。
ViewGroup的焦點(diǎn)查找機(jī)制的核心其實(shí)就是FocusFinder的findNextFocus方法。
主要步驟:
主要注意三點(diǎn):
在addFocusables之后,找到指定方向上與當(dāng)前focused距離最近的view。在進(jìn)行查找之前,會(huì)統(tǒng)一坐標(biāo)系。
總的來說就是根據(jù)當(dāng)前focused的位置以及按鍵的方向,循環(huán)比較focusable集合中哪一個(gè)最適合,然后返回最合適的view,焦點(diǎn)查找就算完成了。
用于比較的方法。分別是將 當(dāng)前聚焦的view , 當(dāng)前遍歷到的focusable 和 目前為止最合適的focusable (i = 0時(shí)是優(yōu)先級(jí)最低的rect)進(jìn)行比較。
判斷是否可以做為候選??梢钥醋魇且粋€(gè)初步篩選的方法,但是到底哪個(gè)更好還需要看beamBeat方法,這個(gè)方法會(huì)將通過篩選的focusable和當(dāng)前最合適的focusable進(jìn)行比較,選出更合適的一個(gè)。
到這里為止ViewGroup的focusSearch方法基本上就講完了。那么下面來看一下RecyclerView的focusSearch方法是如何實(shí)現(xiàn)焦點(diǎn)查找的。
前面講到了,該方法主要是為了解決 RecyclerView聚焦在按鍵方向上、當(dāng)前屏幕區(qū)域內(nèi)可見的最后一個(gè)item時(shí),當(dāng)前不可見的下一個(gè)item將無法獲得焦點(diǎn)。
這個(gè)方法是由LayoutManager來實(shí)現(xiàn)的,這就是RecyclerView的針對上面提到的情況的焦點(diǎn)查找方法。這里主要分析LinearLayoutManager中實(shí)現(xiàn)的該方法,如果在使用其他的LayoutManager時(shí)出現(xiàn)RecyclelerView焦點(diǎn)不符合預(yù)期的話,可以查看對于LayoutManager下的onFocusSearchFailed方法。
主要關(guān)注findPartiallyOrCompletelyInvisibleChildClosestToEnd方法,通過這個(gè)方法的命名我們大致就可以看出來這個(gè)方法的作用了。這個(gè)方法主要會(huì) 根據(jù)當(dāng)前RecyclerVIew的正逆序以及按鍵方向,找出最近一個(gè)部分或完全不可見的View 。
這個(gè)方法是RecyclerView內(nèi)部的方法,和FocusFinder中的isCandidate方法的邏輯可以說幾乎是一摸一樣的。
到此為止ViewGroup的focusSearch和RecyclerVIew的focusSearch都分析完了。我們已經(jīng)知道RecyclerView滑動(dòng)到最底部的時(shí)候,發(fā)生了哪些焦點(diǎn)行為,那么解決起來就比較簡單了。
結(jié)合KeyEvent事件的流轉(zhuǎn),處理焦點(diǎn)的時(shí)機(jī),按照優(yōu)先級(jí)(順序)依次是:
以上任一處都可以指定焦點(diǎn),一旦消費(fèi)了就不再往下走。
比如前面說到了RecyclerView就是通過重寫focusSearch方法對邊界上部分可見或不可見的view的焦點(diǎn)查找進(jìn)行了特殊處理。
重寫RecyclerView的focusSearch方法
相信很多剛接觸AndroidTV開發(fā)的開發(fā)者,都會(huì)被各種焦點(diǎn)問題給折磨的不行。不管是學(xué)技術(shù)還是學(xué)習(xí)其他知識(shí),都要學(xué)習(xí)和理解其中原理,碰到問題我們才能得心應(yīng)手。下面就來探一探Android的焦點(diǎn)分發(fā)的過程。
Android焦點(diǎn)事件的分發(fā)是從ViewRootImpl的processKeyEvent開始的,源碼如下:
源碼比較長,下面我就慢慢來講解一下具體的每一個(gè)細(xì)節(jié)。
dispatchKeyEvent方法返回true代表焦點(diǎn)事件被消費(fèi)了。
ViewGroup的dispatchKeyEvent()方法的源碼如下:
(2)ViewGroup的dispatchKeyEvent執(zhí)行流程
(3)下面再來瞧瞧view的dispatchKeyEvent方法的具體的執(zhí)行過程
驚奇的發(fā)現(xiàn)執(zhí)行了onKeyListener中的onKey方法,如果onKey方法返回true,那么dispatchKeyEvent方法也會(huì)返回true
可以得出結(jié)論:如果想要修改ViewGroup焦點(diǎn)事件的分發(fā),可以這么干:
注意:實(shí)際開發(fā)中,理論上所有焦點(diǎn)問題都可以通過給dispatchKeyEvent方法增加監(jiān)聽來來攔截來控制。
(1)dispatchKeyEvent方法返回false后,先得到按鍵的方向direction值,這個(gè)值是一個(gè)int類型參數(shù)。這個(gè)direction值是后面來進(jìn)行焦點(diǎn)查找的。
(2)接著會(huì)調(diào)用DecorView的findFocus()方法一層一層往下查找已經(jīng)獲取焦點(diǎn)的子View。
ViewGroup的findFocus方法如下:
View的findFocus方法
說明:判斷view是否獲取焦點(diǎn)的isFocused()方法, (mPrivateFlags PFLAG_FOCUSED) != 0 和view 的isFocused()方法是一致的。
其中isFocused()方法的作用是判斷view是否已經(jīng)獲取焦點(diǎn),如果viewGroup已經(jīng)獲取到了焦點(diǎn),那么返回本身即可,否則通過mFocused的findFocus()方法來找焦點(diǎn)。mFocused其實(shí)就是ViewGroup中獲取焦點(diǎn)的子view,如果mView不是ViewGourp的話,findFocus其實(shí)就是判斷本身是否已經(jīng)獲取焦點(diǎn),如果已經(jīng)獲取焦點(diǎn)了,返回本身。
(3)回到processKeyEvent方法中,如果findFocus方法返回的mFocused不為空,說明找到了當(dāng)前獲取焦點(diǎn)的view(mFocused),接著focusSearch會(huì)把direction(遙控器按鍵按下的方向)作為參數(shù),找到特定方向下一個(gè)將要獲取焦點(diǎn)的view,最后如果該view不為空,那么就讓該view獲取焦點(diǎn)。
(4)focusSearch方法的具體實(shí)現(xiàn)。
focusSearch方法的源碼如下:
可以看出focusSearch其實(shí)是一層一層地網(wǎng)上調(diào)用父View的focusSearch方法,直到當(dāng)前view是根布局(isRootNamespace()方法),通過注釋可以知道focusSearch最終會(huì)調(diào)用DecorView的focusSearch方法。而DecorView的focusSearch方法找到的焦點(diǎn)view是通過FocusFinder來找到的。
(5)FocusFinder是什么?
它其實(shí)是一個(gè)實(shí)現(xiàn) 根據(jù)給定的按鍵方向,通過當(dāng)前的獲取焦點(diǎn)的View,查找下一個(gè)獲取焦點(diǎn)的view這樣算法的類。焦點(diǎn)沒有被攔截的情況下,Android框架焦點(diǎn)的查找最終都是通過FocusFinder類來實(shí)現(xiàn)的。
(6)FocusFinder是如何通過findNextFocus方法尋找焦點(diǎn)的。
下面就來看看FocusFinder類是如何通過findNextFocus來找焦點(diǎn)的。一層一層往下看,后面會(huì)執(zhí)行findNextUserSpecifiedFocus()方法,這個(gè)方法會(huì)執(zhí)行focused(即當(dāng)前獲取焦點(diǎn)的View)的findUserSetNextFocus方法,如果該方法返回的View不為空,且isFocusable = true isInTouchMode() = true的話,F(xiàn)ocusFinder找到的焦點(diǎn)就是findNextUserSpecifiedFocus()返回的View。
(7)findNextFocus會(huì)優(yōu)先根據(jù)XML里設(shè)置的下一個(gè)將獲取焦點(diǎn)的View ID值來尋找將要獲取焦點(diǎn)的View。
看看View的findUserSetNextFocus方法內(nèi)部都干了些什么,OMG不就是通過我們xml布局里設(shè)置的nextFocusLeft,nextFocusRight的viewId來找焦點(diǎn)嗎,如果按下Left鍵,那么便會(huì)通過nextFocusLeft值里的View Id值去找下一個(gè)獲取焦點(diǎn)的View。
可以得出以下結(jié)論:
1. 如果一個(gè)View在XML布局中設(shè)置了focusable = true isInTouchMode = true,那么這個(gè)View會(huì)優(yōu)先獲取焦點(diǎn)。
2. 通過設(shè)置nextFocusLeft,nextFocusRight,nextFocusUp,nextFocusDown值可以控制View的下一個(gè)焦點(diǎn)。
Android焦點(diǎn)的原理實(shí)現(xiàn)就這些??偨Y(jié)一下:
為了方便同志們學(xué)習(xí),我這做了張導(dǎo)圖,方便大家理解~