因为工作关系,涉及到很多的安卓物理键盘引起的Focus问题。汗,我估计在Android code这群人里,每天要和Focus问题打交道的少之又少了吧,现在好多应用在设计时很少考虑物理键盘或者蓝牙键盘了,因为应用场景极少,在这少之又少的项目中估计就是我参与的产品了。
言归正传,focus属性在谷歌亲儿子不用物理键盘后基本上没有设计上的升级了吧(个人感觉).且在现在为了界面美观绚丽而进一步复杂的布局设计基础上,focus基本无法按照简单的设计路线走了。在focus问题的debug中付出了很多努力,仍未能窥尽其其竟。
自我总结难点有二:
1.focus的运行在最初设计时就已经定型了,然而focus属性作为一个不主要的附属属性,不被重视,当代码全部code结束后来改它,或者说被要求在代码主体不能大动的前提下改进,难。谁会让你为了这么个不重要的问题改代码,影响了主要功能怎么办,影响了release版本怎么办~
2.focus自有其运行规律,经常为了改动一处,而在八竿子之外的某处会收到影响,改来改去,难上加难矣。
唠叨结束,下面自己总结的干货,若有不对请指正。谢
1.FrameWork对Focus默认处理机制
看android.view.view中一段注释就知道了Foucs的默认机制:
* The framework will handled moving focus in response to user input. To force focus to a specific view, call requestFocus. Framework会根据用户输入处理focus,需要让特定的View获得Focus的时候。调用RequestFocus()。每次有布局隐藏或显示或执行requestFocus后都会刷新布局树。 * The framework will handle routine focus movement in response to user input.This includes changing the focus as views are removed or hidden, or as new views become available. Views indicate their willingness to take focus through the { #isFocusable} method. To change whether a view can take focus, call {#setFocusable(boolean)}. When in touch mode (see notes below) views indicate whether they still would like focus via { #isFocusableInTouchMode} and can change this via {setFocusableInTouchMode(boolean)}. 当view被删除或隐藏后,framework处理Foucs的移动轨迹,通过isFocusableInTouchMode和setFocusableInTouchMode(boolean)来设置view是否可以获得Focus. * Focus movement is based on an algorithm which finds the nearest neighbor in a given direction. In rare cases, the default algorithm may not match the intended behavior of the developer. Focus移动由一个查找相邻或指定方向上的算法控制。但是不一定是开发者希望的样子
下面这条是所有现实问题的基准
* When a user is navigating a user interface via directional keys such as a D-pad, it is necessary to give focus to actionable items such as buttons so the user can see what will take input. If the device has touch capabilities, however, and the user begins interacting with the interface by touching it, it is no longer necessary to always highlight, or give focus to, a particular view. This motivates a mode for interaction named 'touch mode'. foucs处理分为触屏模式和按键模式,这两种模式有不同的Focus显示方式。
关于focus处理的方法一部分是内部调用函数,我们能用的武器是: setFocusable() android:focusable="boolean"是否可以获取focus requestfocus() 获取Focus hasFocus() 判断是否占有Focus onFocusCHangeListener() 监听focu动态
总的来说:Frame会默认处理Focus的显示和移动规则,这些规则适用大部分情况,不适用的就是Bug…
2.问题出现的原因,为什么默认机制不起作用:
“Frame会默认处理Focus的显示和移动规则”这句话就是问题的原因,默认机制只适用于默认的布局。出问题场景基本为两种:
1.基础控件为自定义,如tab,popupwindow,adapter以及较复杂布局之类的;
2.开发者希望按自己的想法移动,可以想象,本来Focus在一条路上走,突然把他拉到另一条路上,这条自定义路走完了,他就不知道去哪了,
3.常见错误场景:
目前常遇到的几种场景:
1. 不同控件件切换,比如从actionBar移动到下面的fragment.
2.自定义控件如TAB间切换。
3.如自定义了focus处理机制,自定义机制的漏洞。经典案例就是设置判断focus移动的精度,移动大于一定距离才会移动focus,在不同分辨率下就出现问题了。
4.其他诡异场景…
4.常用处理方法:
1.使用hierarchyviewer工具读取Focus状态:首先找到Focus在哪,然后分析focus为什么出现在这里并判断是否是布局属性设置错了。
2.分析自定义Focus处理是否有漏洞,这个就具体问题具体分析了,如果没有自定义处理,跳过。
3.使用大杀器requestFocus(),当focus不按照我们希望的路线移动时,可以坚挺按键如
public boolean onKeyDown(KeyEvent key){
if(event == keyevent.KETY_DOWN) view.requestFocus();
}
或者disatchKeyEvent()等坚挺操作的方法
但同时按上面说的,这么改可能会引发其他地方出现Focus问题,所以改时要慎重.
4.控件获得Focus时的显示问题,为了告诉用户foucs在哪里,会在占有foucs的控件上显示边框。如果是这方面的问题纯属布局问题检查的方向:
1.布局中的drawable/selector.
2.边框资源图片。
5.还有一种focus问题称之为诡异问题。这种问题基本无解… 。
总的来说Focus问题场景多变需要灵活处理,如果有新技能再后续更新,待续…