焦点处理相关记录
以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分
需解决问题
控件的下一个焦点是哪?
分析思路
当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理。 在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(...),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
private
void deliverKeyEventPostIme(QueuedInputEvent q) {
...
// Handle automatic focus changes.
if
(event.getAction() == KeyEvent.ACTION_DOWN) {
int
direction = 0 ;
switch
(event.getKeyCode()) {
case
KeyEvent.KEYCODE_DPAD_LEFT:
if
(event.hasNoModifiers()) {
direction = View.FOCUS_LEFT;
}
break ;
case
KeyEvent.KEYCODE_DPAD_RIGHT:
if
(event.hasNoModifiers()) {
direction = View.FOCUS_RIGHT;
}
break ;
...
}
if
(direction != 0 ) {
View focused = mView.findFocus();
if
(focused != null ) {
View v = focused.focusSearch(direction);
if
(v != null
&& v != focused) {
.....
if
(v.requestFocus(direction, mTempRect)) {
...finishInputEvent(q, true );
return ;
}
}
...
}
}
|
1
2
|
View v = focused.focusSearch(direction); v.requestFocus(direction, mTempRect) |
具体分析
在具体分析前,首先我们先明确下相关变量的定义
View mView : 主体View[DecorView]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//一般把主View“DecorView”添加到WindowManagerImpl中(通过addView) //WindowManagerImpl.java private
void addView(View view...) {
ViewRootImpl root;
root = new
ViewRootImpl(view.getContext());
...
root.setView(view, wparams, panelParentView);
...
}<br>
//ViewRootImpl.java public
void setView(View view....) {
synchronized
( this ) {
if
(mView == null ) {
mView = view;
...
}
...
}
} |
View focused :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
View focused = mView.findFocus();<br>
//PhoneWindow.java
private
final class DecorView extends
FrameLayout implements
RootVie.... {
...
}<br>
//FrameLayout.java
public
class FrameLayout extends
ViewGroup {
...
}<br>
//ViewGroup.java
//mFocused记录的是当前被焦点选中的view
@Override
public
View findFocus() {
if
(DBG) {
System.out.println( "Find focus in "
+ this + ": flags="
+ isFocused() + ", child="
+ mFocused);
}
if
(isFocused()) {
return
this ;
}
if
(mFocused != null ) {
return
mFocused.findFocus();
}
return
null ;
} |
在明确的相关变量后,我们开始View v = focused.focusSearch(direction)的具体分析.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//View.java public View focusSearch( int
direction) {
//如果存在父控件,则执行父控件的focusSearch方法 if
(mParent != null ) {
return
mParent.focusSearch( this , direction);
} else
{
return
null ;
}
}
//ViewGroup.java
public
View focusSearch(View focused, int
direction) {
//判断是否为顶层布局,若是则执行对应方法,若不是则继续向上寻找,说明会从内到外的一层层进行判断,直到最外层的布局为止
if
(isRootNamespace()) {
return
FocusFinder.getInstance().findNextFocus( this , focused, direction);
} else
if (mParent != null ) {
return
mParent.focusSearch(focused, direction);
}
return
null ;
}
|
说明在这个过程中,其实是从里层开始一直遍历到最外层布局,然后在最外层布局将处理交给了FocusFinder中的方法.
1
|
FocusFinder.getInstance().findNextFocus( this , focused, direction);
|
1
2
3
4
|
//FocusFinder.java public final View findNextFocus(ViewGroup root, View focused, int
direction) {
return
findNextFocus(root, focused, null , direction);
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//FocusFinder.java private
View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int
direction) {
View next = null ;
if
(focused != null ) {
next = findNextUserSpecifiedFocus(root, focused, direction);
}
if
(next != null ) {
return
next;
}
ArrayList<View> focusables = mTempList;
try
{
focusables.clear();
root.addFocusables(focusables, direction);
if
(!focusables.isEmpty()) {
next = findNextFocus(root, focused, focusedRect, direction, focusables);
}
} finally
{
focusables.clear();
}
return
next;
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
//FocusFinder.java private
View findNextUserSpecifiedFocus(ViewGroup root, View focused, int
direction) {
// check for user specified next focus
View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
if
(userSetNextFocus != null
&& userSetNextFocus.isFocusable()
&& (!userSetNextFocus.isInTouchMode()
|| userSetNextFocus.isFocusableInTouchMode())) {
return
userSetNextFocus;
}
return
null ;
}<br> //View.java View findUserSetNextFocus(View root, int
direction) {
switch
(direction) {
case
FOCUS_LEFT:
if
(mNextFocusLeftId == View.NO_ID) return
null ;
return
findViewInsideOutShouldExist(root, mNextFocusLeftId);
case
FOCUS_RIGHT:
if
(mNextFocusRightId == View.NO_ID) return
null ;
return
findViewInsideOutShouldExist(root, mNextFocusRightId);
case
FOCUS_UP:
if
(mNextFocusUpId == View.NO_ID) return
null ;
return
findViewInsideOutShouldExist(root, mNextFocusUpId);
case
FOCUS_DOWN:
if
(mNextFocusDownId == View.NO_ID) return
null ;
return
findViewInsideOutShouldExist(root, mNextFocusDownId);
case
FOCUS_FORWARD:
if
(mNextFocusForwardId == View.NO_ID) return
null ;
return
findViewInsideOutShouldExist(root, mNextFocusForwardId);
case
FOCUS_BACKWARD: {
if
(mID == View.NO_ID) return
null ;
final
int id = mID;
return
root.findViewByPredicateInsideOut( this , new
Predicate<View>() {
@Override
public
boolean apply(View t) {
return
t.mNextFocusForwardId == id;
}
});
}
}
return
null ;
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
//FocusFinder.java private
View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
int
direction, ArrayList<View> focusables) {
if
(focused != null ) {
if
(focusedRect == null ) {
focusedRect = mFocusedRect;
}
// fill in interesting rect from focused
focused.getFocusedRect(focusedRect);
root.offsetDescendantRectToMyCoords(focused, focusedRect);
} else
{
if
(focusedRect == null ) {
focusedRect = mFocusedRect;
// make up a rect at top left or bottom right of root
switch
(direction) {
case
View.FOCUS_RIGHT:
case
View.FOCUS_DOWN:
setFocusTopLeft(root, focusedRect);
break ;
case
View.FOCUS_FORWARD:
if
(root.isLayoutRtl()) {
setFocusBottomRight(root, focusedRect);
} else
{
setFocusTopLeft(root, focusedRect);
}
break ;
case
View.FOCUS_LEFT:
case
View.FOCUS_UP:
setFocusBottomRight(root, focusedRect);
break ;
case
View.FOCUS_BACKWARD:
if
(root.isLayoutRtl()) {
setFocusTopLeft(root, focusedRect);
} else
{
setFocusBottomRight(root, focusedRect);
break ;
}
}
}
}
switch
(direction) {
case
View.FOCUS_FORWARD:
case
View.FOCUS_BACKWARD:
return
findNextFocusInRelativeDirection(focusables, root, focused, focusedRect,
direction);
case
View.FOCUS_UP:
case
View.FOCUS_DOWN:
case
View.FOCUS_LEFT:
case
View.FOCUS_RIGHT:
return
findNextFocusInAbsoluteDirection(focusables, root, focused,
focusedRect, direction);
default :
throw
new IllegalArgumentException( "Unknown direction: "
+ direction);
}
} |
结论
查找焦点的过程,主要是从View的focusSearch(...)方法开始,从当前焦点开始逐层往外,最终在最外层布局执行FocusFinder中的核心方法来获得下个焦点所在的视图view.
如果需要指定跳转,可以在逐层focusSearch(...)的时候,返回特定的view