android focus查找 方向键如何查找focus

    xiaoxiao2021-03-25  198

    上一篇文章提到了,新起的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

    最新回复(0)