理解焦点处理原理的相关记录

焦点处理相关记录

以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分

需解决问题

控件的下一个焦点是哪?

分析思路

当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理。 在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(...),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法。

private void deliverKeyEventPostIme(QueuedInputEvent q) {
    ...
    // Handle automatic focus changes.
    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;
        ...
      }
      if (direction != 0) {
        View focused = mView.findFocus();
        if (focused != null) {
          View v = focused.focusSearch(direction);
          if (v != null && v != focused) {
             .....
            if (v.requestFocus(direction, mTempRect)) {
              ...finishInputEvent(q, true);
              return;
            }
          }
          ...
        }
      }

由此方法可以看出,最主要的两个核心过程:

View v = focused.focusSearch(direction);
    v.requestFocus(direction, mTempRect)

接下来详细的分析下,看看过程中进行了什么操作

具体分析

在具体分析前,首先我们先明确下相关变量的定义

View mView : 主体View[DecorView]

//一般把主View“DecorView”添加到WindowManagerImpl中(通过addView)
    //WindowManagerImpl.java
      private void addView(View view...) {
        ViewRootImpl root;
        root = new ViewRootImpl(view.getContext());
        ...
        root.setView(view, wparams, panelParentView);
        ...
      }

    //ViewRootImpl.java
    public void setView(View view....) {
      synchronized (this) {
        if (mView == null) {
          mView = view;
          ...
        }
      ...
      }
    }

所以mView是一个DecorView类型的变量.

View focused :

View focused = mView.findFocus();

    //PhoneWindow.java
    private final class DecorView extends FrameLayout implements RootVie.... {
      ...
    }

    //FrameLayout.java
    public class FrameLayout extends ViewGroup {
      ...
    }

    //ViewGroup.java
    //mFocused记录的是当前被焦点选中的view
    @Override
    public View findFocus() {
    if (DBG) {
      System.out.println("Find focus in " + this + ": flags="
          + isFocused() + ", child=" + mFocused);
    }
    if (isFocused()) {
      return this;
    }
    if (mFocused != null) {
      return mFocused.findFocus();
    }
    return null;
  }

所以最终得到的focused为当前页面中得到焦点的view.

在明确的相关变量后,我们开始View v = focused.focusSearch(direction)的具体分析.

//View.java
   public View focusSearch(int direction) {
   //如果存在父控件,则执行父控件的focusSearch方法
    if (mParent != null) {
      return mParent.focusSearch(this, direction);
    } else {
      return null;
    }
  }
  //ViewGroup.java
  public View focusSearch(View focused, int direction) {
    //判断是否为顶层布局,若是则执行对应方法,若不是则继续向上寻找,说明会从内到外的一层层进行判断,直到最外层的布局为止
    if (isRootNamespace()) {
      return FocusFinder.getInstance().findNextFocus(this, focused, direction);
    } else if (mParent != null) {
      return mParent.focusSearch(focused, direction);
    }
    return null;
  }

说明在这个过程中,其实是从里层开始一直遍历到最外层布局,然后在最外层布局将处理交给了FocusFinder中的方法.

FocusFinder.getInstance().findNextFocus(this, focused, direction);

那我们来看看此方法具体做了什么操作

//FocusFinder.java
    public final View findNextFocus(ViewGroup root, View focused, int direction) {
        return findNextFocus(root, focused, null, direction);
    }
//FocusFinder.java
  private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
    View next = null;
    if (focused != null) {
      next = findNextUserSpecifiedFocus(root, focused, direction);
    }
    if (next != null) {
      return next;
    }
    ArrayList<View> focusables = mTempList;
    try {
      focusables.clear();
      root.addFocusables(focusables, direction);
      if (!focusables.isEmpty()) {
        next = findNextFocus(root, focused, focusedRect, direction, focusables);
      }
    } finally {
      focusables.clear();
    }
    return next;
  }

发现在findNextFocus的执行过程的开始,先执行了findNextUserSpecifiedFocus(...)方法,由代码可以看出,此方法先去判断特定Id值是否存在,若存在则查询出Id对应的view.其实这些Id就是xml里通过android:nextFocusUp="..."等或者代码特别指定的焦点顺序.所以在此过程先判断,若存在,说明下个焦点已经找到,直接返回.

//FocusFinder.java
  private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
    // check for user specified next focus
    View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
    if (userSetNextFocus != null && userSetNextFocus.isFocusable()
        && (!userSetNextFocus.isInTouchMode()
            || userSetNextFocus.isFocusableInTouchMode())) {
      return userSetNextFocus;
    }
    return null;
  }

  //View.java
  View findUserSetNextFocus(View root, int direction) {
    switch (direction) {
      case FOCUS_LEFT:
        if (mNextFocusLeftId == View.NO_ID) return null;
        return findViewInsideOutShouldExist(root, mNextFocusLeftId);
      case FOCUS_RIGHT:
        if (mNextFocusRightId == View.NO_ID) return null;
        return findViewInsideOutShouldExist(root, mNextFocusRightId);
      case FOCUS_UP:
        if (mNextFocusUpId == View.NO_ID) return null;
        return findViewInsideOutShouldExist(root, mNextFocusUpId);
      case FOCUS_DOWN:
        if (mNextFocusDownId == View.NO_ID) return null;
        return findViewInsideOutShouldExist(root, mNextFocusDownId);
      case FOCUS_FORWARD:
        if (mNextFocusForwardId == View.NO_ID) return null;
        return findViewInsideOutShouldExist(root, mNextFocusForwardId);
      case FOCUS_BACKWARD: {
        if (mID == View.NO_ID) return null;
        final int id = mID;
        return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
          @Override
          public boolean apply(View t) {
            return t.mNextFocusForwardId == id;
          }
        });
      }
    }
    return null;
  }

如果上面过程没有查询到,则会执行到findNextFocus(...)方法.在这个方法中,先通过offsetDescendantRectToMyCoords(...)方法获得焦点控件的位置矩阵.然后通过比较得到下一个焦点的控件。具体的比较规则可以查看findNextFocusInRelativeDirection(...)方法与findNextFocusInAbsoluteDirection(...)方法.

//FocusFinder.java
  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);
    } else {
      if (focusedRect == null) {
        focusedRect = mFocusedRect;
        // make up a rect at top left or bottom right of root
        switch (direction) {
          case View.FOCUS_RIGHT:
          case View.FOCUS_DOWN:
            setFocusTopLeft(root, focusedRect);
            break;
          case View.FOCUS_FORWARD:
            if (root.isLayoutRtl()) {
              setFocusBottomRight(root, focusedRect);
            } else {
              setFocusTopLeft(root, focusedRect);
            }
            break;
          case View.FOCUS_LEFT:
          case View.FOCUS_UP:
            setFocusBottomRight(root, focusedRect);
            break;
          case View.FOCUS_BACKWARD:
            if (root.isLayoutRtl()) {
              setFocusTopLeft(root, focusedRect);
            } else {
              setFocusBottomRight(root, focusedRect);
            break;
          }
        }
      }
    }
    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);
    }
  }

结论

查找焦点的过程,主要是从View的focusSearch(...)方法开始,从当前焦点开始逐层往外,最终在最外层布局执行FocusFinder中的核心方法来获得下个焦点所在的视图view.

如果需要指定跳转,可以在逐层focusSearch(...)的时候,返回特定的view

转载:http://www.tuicool.com/articles/EjiYRz

时间: 2024-11-09 09:41:02

理解焦点处理原理的相关记录的相关文章

[Android学习笔记]理解焦点处理原理的相关记录

焦点处理相关记录 以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分 需解决问题 控件的下一个焦点是哪? 分析思路 当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理.在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(...),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法. private void deliverKeyEventPostIme

LVS之原理篇--深入全面理解LVS工作原理

一.介绍 LVS是Linux Virtual Server的简写,即Linux虚拟服务器,是一个虚拟的服务器集群系统.该项目由章文嵩博士成立,是中国国内最早出现的自由软件项目之一. 使用LVS技术要达到的目标是:通过LVS提供的负载均衡技术和Linux操作系统实现一个高性能.高可用的服务器群集,它具有良好可靠性.可扩展性和可操作性.从而以低廉的成本实现最优的服务性能. 负载均衡技术有很多实现方案,如基于DNS域名轮流解析的方法.基于客户端调度访问的方法.基于应用层系统负载的调度方法,还有基于IP

java线程总结--synchronized关键字,原理以及相关的锁

在多线程编程中,synchronized关键字非常常见,当我们需要进行"同步"操作时,我们很多时候需要该该关键字对代码块或者方法进行锁定.被synchronized锁定的代码块,只能同时有一条线程访问该代码块. 上面是很多人的认识,当然也是我之前对synchronized关键字的浅显认识,其实上面的观点存在一定的偏差.在参考了很多文章以及自己动手测试过相关代码后,我觉得有必要记录下自己对synchronized关键字的一些理解,在这个过程,会简单说说synchronized关键字的具体

spring相关记录

2018-11-01 ,本文 70 字,阅读全文约需 1 分钟 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping 自定义相关映射 Blog 链表的并发安全 oracle索引 spring相关记录 Oracle的sql优化 责任链模式 双重检查的单例模式为啥需要使用volatile volatile原理 synchronized原理 同步工具类 代理模式 jdk8的新特性 Oracle的各种连接 jvm 工作中遇到的一些

深入理解MySQL优化原理

说起MySQL的查询优化,相信大家收藏了一堆奇技淫巧:不能使用SELECT *.不使用NULL字段.合理创建索引.为字段选择合适的数据类型..... 你是否真的理解这些优化技巧?是否理解其背后的工作原理?在实际场景下性能真有提升吗?我想未必.因而理解这些优化建议背后的原理就尤为重要,希望本文能让你重新审视这些优化建议,并在实际业务场景下合理的运用. MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图

centos7的selinux的原理及相关配置

centos7的selinux的原理及相关配置 SELinux的全称是Security Enhanced Linux, 就是安全加强的Linux.在SELinux之前,root账号能够任意的访问所有文档和服务:如果某个文件设为777,那么任何用户都可以访问甚至删除:这种方式称为DAC(主动访问机制),很不安全. DAC 自主访问控制: 用户根据自己的文件权限来决定对文件的操作,也就是依据文件的own,group,other/r,w,x权限进行限制.Root有最高权限无法限制.r,w,x权限划分太

第82课:Spark Streaming第一课:案例动手实战并在电光石火间理解其工作原理

本期内容: 1.Spark Streaming 动手实战演示 2.闪电般理解Spark Streaming原理 案例动手实战并在电光石火间理解其工作原理 流(Streaming),在大数据时代为数据流处理,就像水流一样,是数据流:既然是数据流处理,就会想到数据的流入.数据的加工.数据的流出. 日常工作.生活中数据来源很多不同的地方.例如:工业时代的汽车制造.监控设备.工业设备会产生很多源数据:信息时代的电商网站.日志服务器.社交网络.金融交易系统.黑客攻击.垃圾邮件.交通监控等:通信时代的手机.

SSH深度历险(十) AOP原理及相关概念学习+AspectJ注解方式配置spring AOP

AOP(Aspect Oriented Programming).是面向切面编程的技术.AOP基于IoC基础.是对OOP的故意补充. AOP之所以能得到广泛应用,主要是由于它将应用系统拆分分了2个部分:核心业务逻辑(Core business concerns)及横向的通用逻辑,也就是所谓的切面Crosscutting enterprise concerns.比如,全部大中型应用都要涉及到的持久化管理(Persistent).事务管理(Transaction Management).权限管理(P

【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)

RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体系的总结,一则进行回顾复习,另则便于查询使用.本图文文档亦源于此.阅读Oracle RAC安装与使用教程前,笔者先对这篇文章整体构思和形成进行梳理.由于阅读者知识储备层次不同,我将从Oracle RAC安装前的准备与规划开始进行整体介绍安装部署Oracle RAC.始于唐博士指导,对数据库集群进行配置安装,前