android 焦点切换原理,[Android学习笔记]理解焦点处理原理的相关记录

焦点处理相关记录

以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分

需解决问题

控件的下一个焦点是哪?

分析思路

当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理。

在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(...),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法。

privatevoiddeliverKeyEventPostIme(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;

}

}

...

}

}

由此方法可以看出,最主要的两个核心过程:

View v = focused.focusSearch(direction);

v.requestFocus(direction, mTempRect)

接下来详细的分析下,看看过程中进行了什么操作

具体分析

在具体分析前,首先我们先明确下相关变量的定义

View mView : 主体View[DecorView]

//一般把主View“DecorView”添加到WindowManagerImpl中(通过addView)

//WindowManagerImpl.java

privatevoidaddView(View view...) {

ViewRootImpl root;

root = new ViewRootImpl(view.getContext());

...

root.setView(view, wparams, panelParentView);

...

}

//ViewRootImpl.java

publicvoidsetView(View view....) {

synchronized (this) {

if (mView == null) {

mView = view;

...

}

...

}

}

所以mView是一个DecorView类型的变量.

View focused :

View focused = mView.findFocus();

//PhoneWindow.java

private final classDecorView extendsFrameLayout implementsRootVie.... {

...

}

//FrameLayout.java

public classFrameLayout extendsViewGroup {

...

}

//ViewGroup.java

//mFocused记录的是当前被焦点选中的view

@Override

publicView 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;

}

所以最终得到的focused为当前页面中得到焦点的view.

在明确的相关变量后,我们开始View v = focused.focusSearch(direction)的具体分析.

//View.java

publicView focusSearch(intdirection) {

//如果存在父控件,则执行父控件的focusSearch方法

if (mParent != null) {

return mParent.focusSearch(this, direction);

} else {

return null;

}

}

//ViewGroup.java

publicView focusSearch(View focused, intdirection) {

//判断是否为顶层布局,若是则执行对应方法,若不是则继续向上寻找,说明会从内到外的一层层进行判断,直到最外层的布局为止

if (isRootNamespace()) {

return FocusFinder.getInstance().findNextFocus(this, focused, direction);

} else if (mParent != null) {

return mParent.focusSearch(focused, direction);

}

return null;

}

说明在这个过程中,其实是从里层开始一直遍历到最外层布局,然后在最外层布局将处理交给了FocusFinder中的方法.

FocusFinder.getInstance().findNextFocus(this, focused, direction);

那我们来看看此方法具体做了什么操作

//FocusFinder.java

publicfinalView findNextFocus(ViewGroup root, View focused, intdirection) {

return findNextFocus(root, focused, null, direction);

}

//FocusFinder.java

privateView findNextFocus(ViewGroup root, View focused, Rect focusedRect, intdirection) {

View next = null;

if (focused != null) {

next = findNextUserSpecifiedFocus(root, focused, direction);

}

if (next != null) {

return next;

}

ArrayList focusables = mTempList;

try {

focusables.clear();

root.addFocusables(focusables, direction);

if (!focusables.isEmpty()) {

next = findNextFocus(root, focused, focusedRect, direction, focusables);

}

} finally {

focusables.clear();

}

return next;

}

发现在findNextFocus的执行过程的开始,先执行了findNextUserSpecifiedFocus(...)方法,由代码可以看出,此方法先去判断特定Id值是否存在,若存在则查询出Id对应的view.其实这些Id就是xml里通过android:nextFocusUp="..."等或者代码特别指定的焦点顺序.所以在此过程先判断,若存在,说明下个焦点已经找到,直接返回.

//FocusFinder.java

privateView findNextUserSpecifiedFocus(ViewGroup root, View focused, intdirection) {

// 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;

}

//View.java

View findUserSetNextFocus(View root, intdirection) {

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() {

@Override

publicbooleanapply(View t) {

return t.mNextFocusForwardId == id;

}

});

}

}

return null;

}

如果上面过程没有查询到,则会执行到findNextFocus(...)方法.在这个方法中,先通过offsetDescendantRectToMyCoords(...)方法获得焦点控件的位置矩阵.然后通过比较得到下一个焦点的控件。具体的比较规则可以查看findNextFocusInRelativeDirection(...)方法与findNextFocusInAbsoluteDirection(...)方法.

//FocusFinder.java

privateView findNextFocus(ViewGroup root, View focused, Rect focusedRect,

intdirection, ArrayList 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值