如何定位解决焦点问题
问题场景:
开发者通常在输入的场景需要通过走焦唤醒键盘。但是在某些场景涉及组件如何主动获焦,监听走焦状态等有疑问。不理解走焦规则,出现实际效果与预期不符合,无法排查组件的走焦状态。
定位手段:
焦点相关概念属性参见gitee:焦点事件
焦点默认行为变更
API 11之前,未配置defaultFocus属性的情况下,当页面首次打开时,原先默认第一个可获焦的非容器组件会立即获取焦点。API 11及之后变更后,该组件不会在此刻获取到焦点,此时焦点在当前页面的根容器上。详情见:默认焦点行为变更
焦点基础能力:
1、一次区分defaultFocus、focusable、enableKeyboardOnfocus
defaultFocus:设置当前组件是否为当前页面上的默认焦点,仅在初次创建的页面第一次进入时生效。详情见:焦点控制-defaultFocus
- 若页面内无任何组件设置defaultFocus为true,页面的默认焦点就是页面的根容器
- 若页面内有多个组件设置defaultFocus为true,则以组件树深度遍历找到的第一个组件为默认焦点
focusable:设置当前组件是否可以获焦。是获焦的能力,不一定当前焦点就在设置的组件上。详情见:焦点控制-focusable
- 存在默认交互逻辑的组件例如button、TextInput等,默认即为可获焦。
- Text、Image等组件默认状态为不可获焦。不可获焦状态下,无法触发焦点事件
如图:Text开启了获焦能力,想要焦点在Text上,要触发主动走焦。
enableKeyboardOnfocus:通过点击以外方式获焦时,控制是否弹出键盘。针对Search、TextInput、TextArea组件的获焦时绑定输入法的行为。
比如:页面初次构建完成时,使TextInput默认获取焦点,但不希望弹出键盘,则可以设置enableKeyboardOnfocus为false。
2、主动获取焦点
若组件本身有获焦能力,默认可获焦。以下方法直接使用;
若组件本身有获焦能力,默认不可获焦。可以添加onClick事件让组件可获焦;
- requestFocus
主动让焦点转移至参数指定的组件上
// 写法一【推荐写法】:
// 更能保障焦点的主动获焦以及有错误码返回
this.getUIContext().getFocusController().requestFocus("testButton")
// 写法二:
focusControl.requestFocus("width_input")
getFocusController是API12新增的UIContext上的方法,有错误码返回
Text(this.text)
.margin({
top: 100
})
.id('TextInput1')
.focusable(true)
.onFocus(() => {
console.log('Text 触发 走焦')
})
Button('设置焦点')
.margin({
top: 200
})
.onClick(() => {
this.getUIContext().getFocusController().requestFocus("TextInput1")
})
现象:用requestFocus即可让Text获取当前焦点,并触发焦点事件。
- focusOnTouch
设置当前组件是否支持点击获焦能力。若组件本身不可获焦,则此功能无效。
Text('focusable' ) // Text设置了focusable初始为true,focusableOnTouch为true
.borderColor(this.color2)
.borderWidth(2)
.width(300)
.height(70)
.focusable(true)
.focusOnTouch(true)
.onFocus(() => {
this.color2 = Color.Blue;
})
.onBlur(() => {
this.color2 = Color.Yellow;
})
现象:焦点在Text上,触发了onFocus监听的获焦回调。
3、主动清除输入框焦点
方法一:设置当前获焦的输入框focusable为false,焦点会转移至页面内下一个可获焦节点。
方法二:clearFocus转移焦点至页面根节点。
清除当前层级页面中的焦点,最终焦点停留在根容器上
this.getUIContext().getFocusController().clearFocus()
4、监听走焦状态
onFocus:组件获取焦点时触发的回调
onBlur:组件失去焦点时触发的回调
一个节点获焦了,说的是它所在的焦点链获焦,也就是它和它的祖先节点都会触发onFocus。详见:焦点事件
定位FAQ
1、显隐切换时焦点控制
TextInput控件的defaultFocus为true时,进入包含该控件的界面就会自动获得焦点。如果该控件在自定义view中,view被if包裹控制显示隐藏,当显示时需要控件获得默认焦点需要怎么样处理?defaultFocus在此种场景不生效。
问题分析:没有理解defaultFocus触发走焦场景。defaultFocus只在页面初次创建且第一次进入才会生效。
解决办法:
// 组件显隐切换时主动控制组件走焦
// 其中testButton为组件id,一定要确保id在应用里具有唯一性
this.getUIContext().getFocusController().requestFocus('testButton')
2、主动获取焦点问题
问题分析:查看二级页面的焦点树,确定跳转到二级页面TextInput没有获焦。场景是版本升级后出现的问题。
解决办法:
二级页面的TextInput组件添加defaultFocus为true的属性,
或者在二级页面用 this.getUIContext().getFocusController().requestFocus('test') 主动获焦。
查看焦点状态
1、获取当前获焦窗口的winId
- 执行 hdc shell hidumper -s WindowManagerService -a '-a'
- 结果如下图,记住红框内的 Focus window ,在步骤2的命令中会用上
2. 输出普通应用焦点树
- 执行 hdc shell "hidumper -s WindowManagerService -a '-w winId -focus'"
3. 规则如下:
- 带* 号的节点都是当前获焦节点
- 带(-)的节点,表示该节点不可获焦
- 不带(-)的节点,表示该节点可获焦
查看焦点与键盘联动日志
实际更复杂的焦点场景,包括了焦点和窗口的联动、焦点唤醒键盘等
- 真机上测试,根据关键词AceFocus|AceKeyboard,过滤出焦点和键盘的日志
- 针对TextInput、TextArea等获焦与键盘联动的场景