requestFocus(int direction, Rect preFocusRec)

和 invalidateO的调用有点相似,requestFocusO也是不能独自完成的,当一个视图想要获取焦点时,
必须请求它的父视图完成该操作,为什么呢?因为父视图知道当前哪个视图正在拥有焦点,如果要进行
焦点切换,则必须先告诉原先的视图放弃焦点,而这些操作所需要的信息是在父视图中保存的,所以
requestFocus()也必须由父视图完成。
该函数有如下三个不同的版本。
• requestFocusO:无参数,它被转换成 requestFocus(View.FOCUS__DOWN)。
• requestFocus(int direction):它被转换成 requestFocus(direction, null)。
• requestFocus(int direction, Rect preFocusRec):第一个参数代表往哪个方向上寻找下一个视图,第
二个参数为当前拥有焦点的视图所占的矩形区,这个区域是相对该视图的直接父视图。
从这三个函数的转换关系可以看出,应用程序中针对某个视图调用requestFocus (无参数 时,并
不一定就会把焦点赋给该视图。因为该函数内部实际上在DOWN方向上找下一个可以获得焦点的视图,
至于是哪个视图就不一定了,这取决于父视图的执行逻辑,这就是为什么该函数的返回值是一个boolean
类型的原因,其意义是该视图到底能不能获得焦点。

 // @return Whether this view or one of its descendants actually took focus.
     * 第一个参数代表往哪个方向上寻找下一个视图,第
     * 二个参数为当前拥有焦点的视图所占的矩形区,这个区域是相对该视图的直接父视图。
     *
     */
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        // need to be focusable
        /**
         * 1 判断该视图是不是FOCUSABLE的,如果不是,则直接返回false
         */
        if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
                (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
        }
**/
        //need to be focusable in touch mode if in touch mode
        /*
         * 如果当前是Touch模式,但是视图的FOCUSABLE_IN—TOUCH_MODE却 为 false,即该视图
         * 不能在 Touch模式下获得焦点,则直接返回false,代码中调用View类 的 isInTouchMode()判断是否是
         * Touch模式
         */
        if (isInTouchMode() &&
                (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
            return false;
        }
        /**
         * 调 用 hasAncestorThatBlockDescendantFocus()判断是否父视图阻止该子视图获得焦点,如果阻
         * 止,则直接返回 false。用程序可以调用 ViewGroup 的 setDescendantFocusability(int focusability)方法设
         * 置该ViewGroup是否阻止其子视图获得焦点,默认情况下都不阻止。
         */
        // need to not have any parents blocking us
        if (hasAncestorThatBlocksDescendantFocus()) {
            return false;
        }
        /**
         * 以上三步实际上执行的都是前期检查,下面将真正进行焦点获取的操作。该步调用
         * handleFocusGainIntemal(dir, rect)进行具体的焦点获取操作,执行完该函数后,则该视图肯定会获取焦点,所以返回true。
         */
        handleFocusGainInternal(direction, previouslyFocusedRect);
        return true;
    }
void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
        if (DBG) {
            System.out.println(this + " requestFocus()");
        }
        /**
         *  1)判断当前窗口是否已经获得焦点,如果已经获得,则直接返回。相当于说,应用程序连续调
         * 用两次requestFocus(),第二次调用时就直接返回了。
         * 2)
         * 如果还没拥有焦点, 则给mPrivateFlags变量中添加FOCUSED标识,这意味着该视图已经真
         * 正拥有焦点了。
         * 3)调用父视图mParent的 requestChildFocus(this, this),第一个参数代表child视图,第二个参数
         * 代 表 focused视 图 。比如,C 中 包 含 B ,B 中 包 含 A ,如 果 从 中 调 用 该 函 数 时 ,就会执行
         * C.requestChildFocus(B, A)。该函数是requestFocusO函数的核心过程,其内部会进行递归调用,并最终
         * 调用到 ViewRoot 中的 requestChildFocus()
         * 4)执行完上一步操作后,该视图的父视图及父父视图都已经获知了本次焦点获取请求,因此,
         * 接下来需要做一些收尾工作了。本步中回调onFocusChanged(),应用程序可以重载该函数以便进行其他
         * 操 作 。这 里 大 家 顺 便 区 分 一 下 两 个 英 语 时 态 的 语 义 ,这 里 回 调 的 是 onFocusChanged(),而不是
         * onFocusChange(),抽象一下就是xxxed(},而 不 是 xxx()。在 Framework的其他地方,有时回调的是
         * onXXXed(),有时却是onXXX(),两者的区别在于前者是当执行完指定操作后才回调,而后者是在指定
         * 操作执行前回调。程序员需要遵守这种函数命名规则,以便调用者能够更清晰地理解代码逻辑
         * 5)调用refreshDrawableState(),因为focus状态改变后,视图的背景图有可能也需要改变。
         */
        if ((mPrivateFlags & FOCUSED) == 0) {
            mPrivateFlags |= FOCUSED;

            if (mParent != null) {
                mParent.requestChildFocus(this, this);
            }

            onFocusChanged(true, direction, previouslyFocusedRect);
            refreshDrawableState();
        }
    }
  • 下面再来具体分析上面第 3 ) 步中提到的requestChildFocus(View child, View focus)函数的内部执 行过程。一般来讲,父视图也是一个ViewGroup对象,直到最后一个父视图,才是 ViewRoot对象,因 此,首先来看ViewGroup中的该函数
 /**
     * {@inheritDoc}
     */
    public void requestChildFocus(View child, View focused) {
        if (DBG) {
            System.out.println(this + " requestChildFocus()");
        }
        /**
         * 判 断 mGroupFlags是否包含FOCUS_BLOCK_DESCENDANTS标识,如果有,则意味着阻止子
         * 视图获得焦点。这一步其实是多余的,因为在前面第3 步中已经进行过同样的判断了,能执行到这里
         * 肯定不会阻止。
         */
        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
            return;
        }
        /**
         * 调 用 super.unFocus()。这里把该ViewGroup当作一个普通的视图处理,因为ViewGroup本身也
         * 是一个View,所 以 ViewGroup本身也可以获取焦点。此处调用super.unFocus()就是当该ViewGroup本
         * 身拥有焦点时,就先让它释放焦点
         */
        // Unfocus us, if necessary
        super.unFocus();
        /**
         * mFocused变量代表了之前拥有焦点的子视图,如果该变量不为空,则需要先通知该视图释放焦
         * 点,此处调用mFocused.unFocus(),然后给该变量赋上新值child。该 child是该ViewGroup的直接子视
         * 图,而不是真正拥有焦点的视图。举个例子,D 中包含C,C 中包含B,B 中包含A,假 设 A 是最终会
         * 获得焦点的视图,那么执行完毕后,
         */
        // We had a previous notion of who had focus. Clear it.
        if (mFocused != child) {
            if (mFocused != null) {
                mFocused.unFocus();
            }

            mFocused = child;
        }
        /**
         * 4)如果该 ViewGroup也有父视图,则递归调用父视图的requestChildFocus(this, focused)。第一个
         * 参数为该ViewGmup对象,每次递归调用时都不同;第二个参数是focused,该参数一直都指向最终应
         * 该获得焦点的视图。
         */
        if (mParent != null) {
            mParent.requestChildFocus(this, focused);
        }
    }

5)以上步骤最终递归到ViewRoot中的requestChildFocus()函数,该函数的执行过程如下


 public void requestChildFocus(View child, View focused) {
        /**
         *  1)调用checkThread()确保是在UI线程中执行该调用
         */
        checkThread();
        /**
         * 如果目标焦点视图就是当前焦点视图,则什么都不用做,ViewRoot中用变量mFocusedView保
         * 存真正拥有焦点的视图。这个判断是多余的,因 为 在 前 面 第 步 第 4) 中已经检查过目标视图是否已
         * 经拥有焦点,所以,能执行到这里, 意味着mFocusedView不是目标焦点视图,于是调用scheduleTraversalO
         * 发起一个View遍历请求
         */
        if (mFocusedView != focused) {
            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
            scheduleTraversals();
        }
        /**
         * 最后,将新的焦点视图赋值给mFocusedView
         */
        mFocusedView = mRealFocusedView = focused;
        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
                + mFocusedView);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中的requestFocus()方法用于请求焦点,即将焦点设置为当前组件。当调用此方法时,当前组件将成为焦点组件,并且将接收键盘输入事件。此方法通常用于将焦点设置为用户需要输入的文本框或按钮等组件。 ### 回答2: Java中的requestFocus()方法用于请求将焦点设置到指定组件上。当调用该方法时,组件将成为焦点所有者,即它将成为用户界面中接收键盘输入的组件。通常,您可以使用requestFocus()方法来强制将焦点设置到指定组件上,以确保该组件首先接收输入。 在GUI程序设计中,焦点控制是非常重要的。当用户使用键盘进行输入时,只能输入到当前有焦点的组件上。如果没有焦点或焦点处于错误的组件上,用户将无法使用键盘进行输入。使用requestFocus()方法可以将焦点设置到正确的组件上,以确保良好的用户交互体验。 此外,当应用程序框架接收到用户输入时,它会按照特定的顺序确定将焦点设置到哪个组件上。如果您希望设置自定义焦点顺序,则可以在用户按下Tab键时使用requestFocus()方法来设置焦点。 总之,requestFocus()方法是Java Swing GUI设计中非常有用的方法,可用于确保正确的焦点控制和用户体验。它可以让您更好地管理焦点控制,提高用户交互体验。 ### 回答3: requestFocus()是Java语言中一个常用的方法之一。它是用来请求某个可获取焦点的组件获得焦点的。在Java的GUI编程中,如果一个组件获得了焦点,则此时用户就可以通过键盘或鼠标对其进行操作。 具体来说,requestFocus()方法的作用就是将某个组件(如文本框、按钮、标签等)的光标设置到组件中,使其成为当前用户所关注的组件。例如,在一个登录界面中,当用户打开时,系统会默认将光标置于用户名的文本框中,让用户能够更方便地输入相关信息。这时,系统就可以通过requestFocus()方法为用户名文本框设置焦点,让其能够获得用户的输入。 除了在GUI编程中使用外,requestFocus()方法还可以在继承Window类的顶级容器中使用。调用requestFocus()方法可以将组件的光标设置为某个特定组件,而不是当前获取焦点的组件。例如,假设您有一个具有许多用户输入字段的应用程序,并且需要在用户输入某些数据之前将光标移动到正确的位置。在这种情况下,requestFocus()方法可以非常有效地实现此操作。 需要注意的是,requestFocus()方法只有在可编辑的组件中才能正确地工作,例如文本框、文本区域和密码字段等等。同时,使用requestFocus()方法提高用户界面的可用性和交互体验的同时,也需要注意防止在应用程序的设计中过度使用该方法,从而导致焦点跳来跳去,使用户感到困惑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值