android 方向键 焦点,android focus查找--方向键如何设置focus

上一篇文章提到了,新起的activity中的某个view是如何获取焦点的。这篇介绍一下,当在一个界面,按下方向键,焦点是如何查找的。

即第一篇文章中跑出来的问题:2.上下按键,焦点如何查找的,它怎么知道下一个获取焦点的是谁?

先总的说一下原理:新起一个界面的时候,会找到一个获取焦点的view(这个view可能是group或者最小的view),当按下方向键时,会进行遍历,根据方向,遍历这一方向布局中的view位置(所在位置是个矩形框),找到最合适的,然后把焦点设置上去。这里主要描述焦点查找,设置不再多说。

一、viewroot中的事件派发

需要input派发的知识,参考博文:

输入事件,在dispatch的时候,输入法不处理,则要拍发给具体的view了,这个过程在viewroot中。

private int processKeyEvent(QueuedInputEvent q) {

final KeyEvent event = (KeyEvent)q.mEvent;

// Deliver the key to the view hierarchy.

if (mView.dispatchKeyEvent(event)) {

return FINISH_HANDLED;//输入法处理了,则直接返回,否则继续

}

//...

// Handle automatic focus changes.

if (event.getAction() == KeyEvent.ACTION_DOWN) {//将键盘事件转换一下,上下左右专程对应的FOCUS_LEFT...

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;

case KeyEvent.KEYCODE_DPAD_UP:

if (event.hasNoModifiers()) {

direction = View.FOCUS_UP;

}

break;

case KeyEvent.KEYCODE_DPAD_DOWN:

if (event.hasNoModifiers()) {

direction = View.FOCUS_DOWN;

}

break;

case KeyEvent.KEYCODE_TAB:

if (event.hasNoModifiers()) {

direction = View.FOCUS_FORWARD;

} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {

direction = View.FOCUS_BACKWARD;

}

break;

}

if (direction != 0) {

View focused = mView.findFocus();//拿到当前处于focus的view,需要以此view为出发点来查找某个方向上的焦点

if (focused != null) {

View v = focused.focusSearch(direction);//查找direction方向上的焦点,direction是上面转换了的(FOCUS_LEFT...)

//...

return FORWARD;

}

二、view和viewgroup的遍历

遍历,找到根节点:mView.findFocus函数走到view--viewgroup中

public View focusSearch(@FocusRealDirection int direction) {

if (mParent != null) {

return mParent.focusSearch(this, direction);

} else {

return null;

}

}主要是viewgroup中的。

/**

* Find the nearest view in the specified direction that wants to take

* focus.

*

* @param focused The view that currently has focus

* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and

* FOCUS_RIGHT, or 0 for not applicable.

*/

@Override

public View focusSearch(View focused, int direction) {

if (isRootNamespace()) {//是根节点了,则开始根据方向查找和是获取焦点的view

// root namespace means we should consider ourselves the top of the

// tree for focus searching; otherwise we could be focus searching

// into other tabs. see LocalActivityManager and TabHost for more info

return FocusFinder.getInstance().findNextFocus(this, focused, direction);//调用focusfinder查找

} else if (mParent != null) {

return mParent.focusSearch(focused, direction);//不是根节点,需要继续遍历,找到根节点。

}

return null;

}遍历类似了,直接focusfinder中的findnextfocus

三、focusFinder

这里我们把过程分成几个步骤:

1.把根节点中的isfocusable的view都抓起来(arraylist)

/**

* Find the next view to take focus in root's descendants, starting from the view

* that currently is focused.

* @param root Contains focused. Cannot be null.

* @param focused Has focus now.

* @param direction Direction to look.

* @return The next focusable view, or null if none exists.

*/

public final View findNextFocus(ViewGroup root, View focused, int direction) {

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

}注释比较清楚。

跳到具体函数

private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {

View next = null;

if (focused != null) {

next = findNextUserSpecifiedFocus(root, focused, direction);//优先查找xml或者code中写定的查找顺序,

//可以指定xml中的属性android:nextFocusUp或code中setNextFocusUpId

}

if (next != null) {//如果xml中没有则根据view的rect继续查找

return next;

}

ArrayList focusables = mTempList;

try {

focusables.clear();

root.addFocusables(focusables, direction);//这里也是一个遍历,找到所有isfocusable的view,把这些view add进来,这里不再扩展了.

if (!focusables.isEmpty()) {//这个arraylist中不是空的,就开始找最接近的view

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

}

} finally { focusables.clear(); } return next; }上面代码看到,查找有先后,先xml中的写定的位置顺序,如果没有则在通过rect来。第一步xml中的查找比较简单,也不会出错,code就几个函数,灰常简单,不再多说,而且这种场景非常少。

2.抓起来,就要统一换上囚服了(统一坐标系)

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

int direction, ArrayList focusables) {

if (focused != null) {

if (focusedRect == null) {

focusedRect = mFocusedRect;

}

// fill in interesting rect from focused

focused.getFocusedRect(focusedRect);

root.offsetDescendantRectToMyCoords(focused, focusedRect);//需要将当前的rect转换,到root中的坐标系中。

//rect实际是坐标点,这里需要转换都根root,viewgroup中的focussearch中是遍历到根节点,以便统一坐标并从全局统筹去做做焦点派发。不然在一个viewgroup里,焦点就永远出不去了。

} else { //......这部分忽略 }

}

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

}

}

3.fire in the hole:开始了详细的盘问

View findNextFocusInAbsoluteDirection(ArrayList focusables, ViewGroup root, View focused,

Rect focusedRect, int direction) {

// initialize the best candidate to something impossible

// (so the first plausible view will become the best choice)

mBestCandidateRect.set(focusedRect);//给最终找到焦点的view配套用的rect,记录最合适的view的坐标

switch(direction) {//记录用的,先给它移动一个身位,以便于比较时记录最合适的位置。

case View.FOCUS_LEFT:

mBestCandidateRect.offset(focusedRect.width() + 1, 0);

break;

case View.FOCUS_RIGHT:

mBestCandidateRect.offset(-(focusedRect.width() + 1), 0);

break;

case View.FOCUS_UP:

mBestCandidateRect.offset(0, focusedRect.height() + 1);

break;

case View.FOCUS_DOWN:

mBestCandidateRect.offset(0, -(focusedRect.height() + 1));

}

View closest = null;

int numFocusables = focusables.size();

for (int i = 0; i < numFocusables; i++) {//开始遍历囚犯名单 arraylist

View focusable = focusables.get(i);

// only interested in other non-root views

if (focusable == focused || focusable == root) continue;

// get focus bounds of other view in same coordinate system

focusable.getFocusedRect(mOtherRect);

root.offsetDescendantRectToMyCoords(focusable, mOtherRect);//统一坐标

if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {//比较找多最合适的,方向上最近的

mBestCandidateRect.set(mOtherRect);//找到了,更新

closest = focusable;//找到了,更新

}

}

return closest;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值