上一篇文章提到了,新起的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<View> 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<View> 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<View> 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;
}
转载请注明原文地址: https://ju.6miu.com/read-1695.html