android焦点分析

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

内容简介:在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


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

查看所有标签

猜你喜欢:

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

Advanced Web Metrics with Google Analytics

Advanced Web Metrics with Google Analytics

Brian Clifton / Sybex / 2008 / USD 39.99

Are you getting the most out of your website? Google insider and web metrics expert Brian Clifton reveals the information you need to get a true picture of your site's impact and stay competitive usin......一起来看看 《Advanced Web Metrics with Google Analytics》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具