平常做手機(jī)開發(fā)的時(shí)候不太需要關(guān)心焦點(diǎn)獲取,但是如果做Android Tv或盒子開發(fā)的時(shí)候就要做好View之間焦點(diǎn)切換了。
View焦點(diǎn)流程圖

viewfocus.jpeg
源碼分析
view通過(guò)以下幾個(gè)方法來(lái)獲取焦點(diǎn)
- View.java -> requestFocus(int direction, Rect previouslyFocusedRect)
public final boolean requestFocus() {
return requestFocus(View.FOCUS_DOWN);
}
public final boolean requestFocus(int direction) {
return requestFocus(direction, null);
}
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return requestFocusNoSearch(direction, previouslyFocusedRect);
}
從代碼中可以看到最終都會(huì)調(diào)到requestFocusNoSearch方法
- view.java -> requestFocusNoSearch(int direction, Rect previouslyFocusedRect)
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// 先判斷當(dāng)前視圖是否具有獲取焦點(diǎn)的能力,如果沒有獲取焦點(diǎn)的能力或沒有顯示則直接返回false
if ((mViewFlags & FOCUSABLE) != FOCUSABLE
|| (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
// 判斷是否在Touch模式下,如果是在touch模式下,就必須要有Touch模式下獲取焦點(diǎn)的能力(FOCUSABLE_IN_TOUCH_MODE)
if (isInTouchMode() &&
(FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
return false;
}
// 判斷父容器是否攔截當(dāng)前視圖獲取焦點(diǎn)(注意:是一層一層遍歷所有的父容器不是只判斷當(dāng)前視圖的直接父容器),如果某一個(gè)父容器阻止則直接返回false,可以通過(guò)設(shè)置setDecendantFocusability(int focusability)方法來(lái)設(shè)置ViewGroup是否阻止子視圖獲取焦點(diǎn),默認(rèn)是不阻止的
if (hasAncestorThatBlocksDescendantFocus()) {
return false;
}
//真正獲取焦點(diǎn)的函數(shù),執(zhí)行完后返回true
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
- View.java -> handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect)
void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " requestFocus()");
}
//先判斷是否已獲取焦點(diǎn),防止多次調(diào)用
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
//設(shè)置當(dāng)前視圖狀態(tài)
mPrivateFlags |= PFLAG_FOCUSED;
View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
if (mParent != null) {
//調(diào)用父容器的requestChildFocus方法,第一個(gè)參數(shù)為當(dāng)前視圖,第二個(gè)視圖為真正要獲取的焦點(diǎn)的視圖
mParent.requestChildFocus(this, this);
updateFocusedInCluster(oldFocus, direction);
}
if (mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
//執(zhí)行回調(diào)方法,并且觸發(fā)invalidate進(jìn)行刷新
onFocusChanged(true, direction, previouslyFocusedRect);
//更新繪制狀態(tài)
refreshDrawableState();
}
}
- ViewGroup.java ->requestChildFocus(View child, View focused)
public void requestChildFocus(View child, View focused) {
if (DBG) {
System.out.println(this + " requestChildFocus()");
}
//先判斷DescendantFocusability是否為FOCUS_BLOCK_DESCENDANTS, 也就是是否阻止子視圖獲取焦點(diǎn),如果阻止則直接跳出
if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
return;
}
// 釋放當(dāng)前視圖的焦點(diǎn)
super.unFocus();
// mFocused是之前有焦點(diǎn)的子視圖,如果之前獲取焦點(diǎn)的視圖和當(dāng)前要獲取焦點(diǎn)視圖不是同一個(gè)的話就更新
if (mFocused != child) {
if (mFocused != null) {
//釋放焦點(diǎn)
mFocused.unFocus(focused);
}
//重新賦值
mFocused = child;
}
//逐層上調(diào)requestChildFocus方法,直到View樹的root節(jié)點(diǎn)
if (mParent != null) {
mParent.requestChildFocus(this, focused);
}
}
ViewGroup獲取焦點(diǎn)處理
ViewGroup獲取焦點(diǎn)比View獲取焦點(diǎn)要稍微復(fù)雜點(diǎn)多了分發(fā)機(jī)制
- ViewGroup.java -> requestFocus(int direction, Rect previouslyFocusedRect)
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " ViewGroup.requestFocus direction="
+ direction);
}
//獲取當(dāng)前焦點(diǎn)分發(fā)機(jī)制
int descendantFocusability = getDescendantFocusability();
switch (descendantFocusability) {
case FOCUS_BLOCK_DESCENDANTS: //阻止子View獲取焦點(diǎn)
return super.requestFocus(direction, previouslyFocusedRect);
case FOCUS_BEFORE_DESCENDANTS: { //先交給自身獲取焦點(diǎn),如果自身不處理則交給子視圖處理(默認(rèn))
final boolean took = super.requestFocus(direction, previouslyFocusedRect);
return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
}
case FOCUS_AFTER_DESCENDANTS: { //先交給子視圖處理,如果子不處理再交給自身處理
final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
return took ? took : super.requestFocus(direction, previouslyFocusedRect);
}
default:
throw new IllegalStateException("descendant focusability must be "
+ "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ "but is " + descendantFocusability);
}
}
onRequestFocusInDescendants方法是調(diào)用子視圖獲取焦點(diǎn)的方法,默認(rèn)是按照子視圖順序處理,direction如果向下或向右則從第一個(gè)開始,如果向上或向左則從最后一個(gè)開始遍歷,直到某個(gè)子視圖獲取焦點(diǎn)。