Android移动应用中的焦点分析
发布时间:2025-05-13 00:01:23 发布人:远客网络
一、Android移动应用中的焦点分析
1、简单一点理解,在移动应用中,焦点就是当前正在处理事件的位置。在手机应用中,最有可能用到焦点的就是EditText,如果同一个界面中有多个EditText,通常情况下同一时间只有一个能够输入内容,此时,这个EditText就获取了焦点。
2、在Android中,对焦点的设置分为两种情况,TouchMode和非TouchMode。现在的手机基本都是触摸屏,我们用手指触摸屏幕来操作Android应用时,处于TouchMode。除了TouchMode之外,还有非TouchMode,利用外接设备来操作应用。比如键盘。使用Genymotion模拟器的时候,一个界面上有多个控件时,可以用电脑tab键来进行移动,被选中的控件会高亮显示,这时候就是非TouchMode,被选中的控件获得了焦点。
3、在手机应用中,用到焦点的时候并不多,但是TV应用中,需要用遥控器来操作选中控件,这时候就需要对焦点进行处理了。关于焦点,常用方法如下:
4、在View类中, isFocusable()和 isFocusableInTouchMode()获取到的结果都是false,也就是说,直接继承自View的控件是不能获取焦点的。我们常用控件中对这两个方法进行了改写,比如EditText,这两个方法都是true,而Button则只有 isFocusable()返回true。这也就是为什么我们用tab键选取Button的时候能够高亮显示,而鼠标点击(模拟触控)的时候不能高亮显示的原因了。如果想在点击的时候也能高亮显示Button,需要手动设置 setFocusableInTouchMode(true),就可以了。
5、如果想对控件的焦点状态进行监听,需要设置 setOnFocusChangeListener(),只要控件的焦点状态发生变化(获得或者失去焦点),都会调用 onFocusChange方法
6、关于焦点的移动,默认的算法会寻找指定方向上最近的可以获取焦点的元素(非TouchMode)。另外在创建控件的时候,也可以指定寻找焦点的方向,设置nextFocusDown、nextFocusLeft、nextFocusRight和 nextFocusUp的值为指定元素就可以了。看以下例子:
7、这里指定了上面的button向上寻找焦点时,下一个元素是id为bottom的元素,也就是说,上面的Button在获取了焦点之后,继续按向上键,系统会将焦点移动到id为bottom的元素上,而不是继续向上。
8、在开发手机应用的过程中,对焦点的处理并不多,它与事件是两个不同的体系,通常情况下焦点和事件是相互独立并不冲突。但是在Button的点击事件中会有一点问题。如果我们队一个button设置了 setFocusableInTouchMode(true),使他可以获取焦点,那么我们点击这个button的时候,第一次点击并不会执行 onClick()方法,而是执行 onFocusChange()。第二次点击的时候才会执行 onClick()方法。看起来好像 onFocusChange()消耗了点击事件,实际上并不是的。
9、这个问题我们看一下源码就清楚了:
10、 onClick()方法是在onTouchEvent的ACTION_UP里调用的,看一下View的onTouchEvent方法:
11、可以看到,只有当focusTaken为false的时候才会执行onClick,focusTaken的值默认是false的,但是在 isFocusable()&& isFocusableInTouchMode()&&!isFocused()为true的时候,会去 requestFocus获取焦点,并将值赋给focusTaken。
12、关键在于 isFocused(),如果当前Button没有获取焦点, isFocused()返回false,!isFocused()值为ture,Button就会去获取焦点,从而导致 focusTaken为true, onClick方法就不会执行了,只有Button已经获取了焦点的时候才会执行onClick方法。
二、android某一app设为焦点app
1、在做电视机顶盒应用的时候,每个控件都是有焦点变化的显示的,如果当前焦点被一个自己不知道的控件获取到,则其他控件就不会获取到焦点,我们必须要找到当前获取焦点的控件,将其 android:focusable="false",不让它获取到焦点。
2、通过findFocus()方法来获取到当前的控件。
3、在Activity的onCreate方法中,启动线程。
4、Runnable run2= new Runnable(){
5、} catch(InterruptedException e){
6、View rootview= DetailActivity.this.getWindow().getDecorView();
7、View aaa= rootview.findFocus();
8、这样每隔5秒会获取一次当前有焦点的控件,打印出log就可以了。
9、最后就是控件的id了,这样你就找到了当前获取焦点的控件了。
10、链接:
三、android tv常见问题(一)焦点查找规律
1、
2、 Recyclerview聚焦到最后一个Item,继续按下键,焦点保持不变。
3、 Recyclerview聚焦到最后一个Item,继续按下键,焦点会跳出RecyclerView,跳到附近的View上。
4、那么当Recyclerview滑动到最底部时,按下键,Android系统是如何找到下一个需要被聚焦的view的呢?我们把断点打在ViewGroup的focusSearch方法上,可以看到从ViewRootImp的performFocusNavigation方法开始,依次调用了如下方法。
5、 View并不会直接去找焦点,而是交给它的parent去找。
6、焦点会逐级的交给父ViewGroup的focusSearch方法去处理,直到最外层的布局,最后实际上是调用了FocusFinder的findNextFocus方法去寻找新的焦点。
7、但是这里要注意的是,RecyclerView和其他的ViewGroup不一样,它自己重写了focusSearch方法。所以在焦点查找委托到达到DecorView之前,会先执行RecyclerView的focusSearch方法。
8、那么,RecyclerView和其他ViewGroup在寻找焦点方面有什么不一样呢?为什么RecyclerView要重写ViewGroup的焦点查找机制呢?想知道这些问题的答案,那我们首先要知道ViewGroup的焦点查找机制。
9、 ViewGroup的焦点查找机制的核心其实就是FocusFinder的findNextFocus方法。
10、在addFocusables之后,找到指定方向上与当前focused距离最近的view。在进行查找之前,会统一坐标系。
11、总的来说就是根据当前focused的位置以及按键的方向,循环比较focusable集合中哪一个最适合,然后返回最合适的view,焦点查找就算完成了。
12、用于比较的方法。分别是将当前聚焦的view,当前遍历到的focusable和目前为止最合适的focusable(i= 0时是优先级最低的rect)进行比较。
13、判断是否可以做为候选。可以看作是一个初步筛选的方法,但是到底哪个更好还需要看beamBeat方法,这个方法会将通过筛选的focusable和当前最合适的focusable进行比较,选出更合适的一个。
14、到这里为止ViewGroup的focusSearch方法基本上就讲完了。那么下面来看一下RecyclerView的focusSearch方法是如何实现焦点查找的。
15、前面讲到了,该方法主要是为了解决 RecyclerView聚焦在按键方向上、当前屏幕区域内可见的最后一个item时,当前不可见的下一个item将无法获得焦点。
16、这个方法是由LayoutManager来实现的,这就是RecyclerView的针对上面提到的情况的焦点查找方法。这里主要分析LinearLayoutManager中实现的该方法,如果在使用其他的LayoutManager时出现RecyclelerView焦点不符合预期的话,可以查看对于LayoutManager下的onFocusSearchFailed方法。
17、主要关注findPartiallyOrCompletelyInvisibleChildClosestToEnd方法,通过这个方法的命名我们大致就可以看出来这个方法的作用了。这个方法主要会根据当前RecyclerVIew的正逆序以及按键方向,找出最近一个部分或完全不可见的View。
18、这个方法是RecyclerView内部的方法,和FocusFinder中的isCandidate方法的逻辑可以说几乎是一摸一样的。
19、到此为止ViewGroup的focusSearch和RecyclerVIew的focusSearch都分析完了。我们已经知道RecyclerView滑动到最底部的时候,发生了哪些焦点行为,那么解决起来就比较简单了。
20、结合KeyEvent事件的流转,处理焦点的时机,按照优先级(顺序)依次是:
21、以上任一处都可以指定焦点,一旦消费了就不再往下走。
22、比如前面说到了RecyclerView就是通过重写focusSearch方法对边界上部分可见或不可见的view的焦点查找进行了特殊处理。
23、重写RecyclerView的focusSearch方法