android焦点分析

栏目: IOS · Android · 发布时间: 5年前

内容简介:在tv端开发中,焦点处理是一个非常重要的技术。该篇主要是想整理相关知识。(本文档依据sdk26进行分析)在android的绘制流程中**ViewRootImpl#performTraversals()**起着关键的作用,而焦点状态也会通过影响视图的绘制。下面来看看android事如何进行第一次寻焦的

  在tv端开发中,焦点处理是一个非常重要的技术。该篇主要是想整理相关知识。(本文档依据sdk26进行分析)

第一次寻焦

  在android的绘制流程中**ViewRootImpl#performTraversals()**起着关键的作用,而焦点状态也会通过影响视图的绘制。

  下面来看看android事如何进行第一次寻焦的

private void performTraversals() {
    ......
    if (mFirst && sAlwaysAssignFocus) {
          // handle first focus request
          if (mView != null) {
              if (!mView.hasFocus()) {
                  mView.restoreDefaultFocus();
              }
          }
    }
    ......
}
复制代码

   mView.restoreDefaultFocus() 将会去查找当前试图第一个可聚焦的View。将会执行 requestFocus(int direction, Rect previouslyFocusedRect) 。因为ViewGroup重写了该方法,增加了是否拦截焦点处理的逻辑,下面我们先来看看 ViewGroup#requestFocus(int direction, Rect previouslyFocusedRect)

@Override
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        if (DBG) {
            System.out.println(this + " ViewGroup.requestFocus direction="
                    + direction);
        }
        int descendantFocusability = getDescendantFocusability();

        switch (descendantFocusability) {
            //拦截焦点,不管当前View是否被聚焦,子View一定获取不到焦点。
            case FOCUS_BLOCK_DESCENDANTS:
                return super.requestFocus(direction, previouslyFocusedRect);
            //在子View之前判断是否应被聚焦,如果为false则会去判断其子View
            case FOCUS_BEFORE_DESCENDANTS: {
                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
            }
            // 在子View判断焦点之后判断
            case FOCUS_AFTER_DESCENDANTS: {
                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
            }
            default:
                throw new IllegalStateException("descendant focusability must be "
                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
                        + "but is " + descendantFocusability);
        }
    }
复制代码

  接着我们来看看 View#requestFocus(int direction, Rect previouslyFocusedRect)

public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
   return requestFocusNoSearch(direction, previouslyFocusedRect);
}

private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
    // 判断是否可被聚焦
    if ((mViewFlags & FOCUSABLE) != FOCUSABLE
            || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
        return false;
    }

    // 判断触摸状态下是否可被聚焦
    if (isInTouchMode() &&
        (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
           return false;
    }

    // 判断父View是否需要拦截
    if (hasAncestorThatBlocksDescendantFocus()) {
        return false;
    }
    //执行聚焦操作
    handleFocusGainInternal(direction, previouslyFocusedRect);
    return true;
}

void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
    if (DBG) {
        System.out.println(this + " requestFocus()");
    }

    if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
        //设置聚焦标志位
        mPrivateFlags |= PFLAG_FOCUSED;

        View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;

        if (mParent != null) { 
            //通知父容器改变焦点View
            mParent.requestChildFocus(this, this);
            updateFocusedInCluster(oldFocus, direction);
        }

        if (mAttachInfo != null) {
            //全局监听回调
            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
        }
        //执行焦点变化与强制绘制
        onFocusChanged(true, direction, previouslyFocusedRect);
        refreshDrawableState();
    }
}
复制代码

遥控器方向键后的寻焦逻辑

  首先会根据按键生成一个寻焦方向(可以查看ViewRootImpl#ViewPostImeInputStage#processKeyEvent)

private int processKeyEvent(QueuedInputEvent q) {
            ......
            //上面是判断是否处理当前按键,如dispatchKeyEvent返回true则不会执行下面的焦点逻辑

            // 根据当前事件生成一个寻焦方向。
            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;
                    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,根据当前view查询下一个方向的聚焦view
                    View focused = mView.findFocus();
                    if (focused != null) {
                    //由此可见,focusSearch 为寻焦的主要方法,可重新该方法来修改焦点逻辑
                        View v = focused.focusSearch(direction);
                        if (v != null && v != focused) {
                            // do the math the get the interesting rect
                            // of previous focused into the coord system of
                            // newly focused view
                            focused.getFocusedRect(mTempRect);
                            if (mView instanceof ViewGroup) {
                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                        focused, mTempRect);
                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(
                                        v, mTempRect);
                            }
                            if (v.requestFocus(direction, mTempRect)) {
                                playSoundEffect(SoundEffectConstants
                                        .getContantForFocusDirection(direction));
                                return FINISH_HANDLED;
                            }
                        }

                        // Give the focused view a last chance to handle the dpad key.
                        if (mView.dispatchUnhandledMove(focused, direction)) {
                            return FINISH_HANDLED;
                        }
                    } else {
                        // find the best view to give focus to in this non-touch-mode with no-focus
                        View v = focusSearch(null, direction);
                        if (v != null && v.requestFocus(direction)) {
                            return FINISH_HANDLED;
                        }
                    }
                }
            }
            return FORWARD;
        }
复制代码

下面可查看View#focusSearch与ViewGroup#focusSearch 的相关处理

View.java

    public View focusSearch(@FocusRealDirection int direction) {
        if (mParent != null) {
            return mParent.focusSearch(this, direction);
        } else {
            return null;
        }
    }
    
ViewGroup.java
    public View focusSearch(View focused, int direction) {
        if (isRootNamespace()) {
            // 通过FocusFinder来查找下一个聚焦view
            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
        } else if (mParent != null) {
            return mParent.focusSearch(focused, direction);
        }
        return null;
    }

复制代码

FocusFinder 是一个焦点处理的类,主要用于在一个方向上,通过当前view与聚焦方案来合理判断下一个被聚焦view


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java5.0Tiger程序高手秘笈

Java5.0Tiger程序高手秘笈

BrettMclaughlin / 东南大学出版社 / 2005-10 / 28.00元

代号为 “Tiger”的下一个 Java 版本,不只是个小改动版。在语言核心中有超过 100 项以上的变动,同时有大量的对 library 与 API 所做的加强,让开发者取得许多新的功能、工具与技术。但在如此多的变化下,应该从何处开始着手?也许可以从既长又无趣的语言规范说明书开始看起;或等待最少 500 页的概念与理论巨著出版;甚至还可以直接把玩新的 JDK 看看能够有什么发现;或者借由《Jav......一起来看看 《Java5.0Tiger程序高手秘笈》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具