arkui-x stack叠层解决焦点变换带来的布局问题
测试板rk3588,arkui-x API Version 12
首先一些概念可参考华为官方文档:焦点导航-人机交互 - 华为HarmonyOS开发者
1、焦点变换的方案
.id(info.appName)
.onFocus(() => {
focusControl.requestFocus(info.appName)
this.isFocusItem[this.lastFocusIndex] = false
this.isFocusItem[info.index] = true
this.lastFocusIndex = info.index
})
.onHover((isHover?: boolean) => {
if (isHover) {
focusControl.requestFocus(info.appName)
this.isFocusItem[this.lastFocusIndex] = false
this.isFocusItem[info.index] = true
this.lastFocusIndex = info.index
// console.log("---AllAppPage", "on hover, index: " + info.index)
}
})
this.isFocusItem记录列表里每一个iterm是否为焦点。实际
.id用来与focusControl.requestFocus配合使用。
这样的方案既支持鼠标悬停触发变焦,也支持tab键+方向键等键盘操作。
在鼠标接pc和鼠标接测试板的两种场景,鼠标和键盘操作都能够做到正常变焦。
这里的方案也填了前面一篇博文的坑:arkui-x开发中遇到的各种奇怪focus问题_arkui-x 坑-CSDN博客

2、焦点变换的效果以及带来的问题
焦点变换的样式可参考焦点-遥控器及焦点-智慧屏 - 华为HarmonyOS开发者
官方文档里提到了动画效果,本篇博文暂不谈动画这块,后面博文再讲。
这里的示例只讲放大比例105%如何实现,和引入的情况。
2.1 attributeModifier实现边框效果
实现方式,采用attributeModifier可以动态设置组件的属性,根据不同状态调整组件属性。
// focusStyle.ets
export class focusBorderNormal implements AttributeModifier<CommonAttribute> {
applyNormalAttribute(instance: CommonAttribute): void {
instance.border({
width: 0,
color:Color.Transparent,
radius: getLength(Const.DEFAULT_5),
})
}
}
export class focusThirdPartyInfoBorderRd28 implements AttributeModifier<CommonAttribute> {
displayInfo: DisPlayInfo = new DisPlayInfo()
applyNormalAttribute(instance: CommonAttribute): void {
instance.border({
width: this.displayInfo.getHeight(4),
color: Color.White,
radius: this.displayInfo.getHeight(Const.DEFAULT_28),
})
instance.padding(this.displayInfo.getHeight(Const.DEFAULT_4))
}
}
// example.ets
......
private modifier: focusThirdPartyInfoBorderRd28 = new focusThirdPartyInfoBorderRd28()
private modifierNormal: focusBorderNormal = new focusBorderNormal()
......
Stack() {
}
.attributeModifier(this.isFocusItem[info.index] ? this.modifier : this.modifierNormal)
......
applyNormalAttribute(instance: CommonAttribute)可以说是一种封装性、复用性很好的重构方法。比起@extend、@style,它可以方便的支持跨对象复用。相关用法也有很多,后面有机会再研究。
这里的用法,表示this.isFocusItem[info.index]为true,即焦点激活,则this.modifier样式会被使用。其对应的focusThirdPartyInfoBorderRd28表示会在iterm外面padding间隔4px处,增加一个宽4px、border radius为28px的边框,如下图,焦点选中了HelloWorld对应的iterm:

2.2 选中iterm变大5%
参考如下写法,焦点选中时,背景色变大5%,图标变大10%。
Stack() {
Text()
.height(135 + (this.isFocusItem[info.index] ? 7 : 0))
.width(241 + (this.isFocusItem[info.index] ? 12 : 0))
.borderRadius(24)
.backgroundColor('#' + info.mainColor)
Image($r(info.appIcon) : 'file://' + info.appIcon)
.height(67 + (this.isFocusItem[info.index] ? 7 : 0)) // 放大10%
.width(85 + (this.isFocusItem[info.index] ? 9 : 0))
.objectFit(ImageFit.ScaleDown)
}
带来的问题:
页面布局采用的是iterm之间相对布局,用margin来定义,如下:
.margin({ right: 4, bottom: 22 })
当焦点激活时,因为在iterm外增加了一个边框,且iterm变大5%,会导致挤压旁边的iterm,通过margin推动周围iterm,导致联动性的,所有iterm都移动。
3、引入stack叠层方便解决布局联动问题
思路很简单,就是用stack定义一个更大的text,能包含变大的iterm + 边框,那么焦点激活时,就只在这个text内缩放,不会挤压推动周围的iterm。
如下,在Column、Stack组件外面,又增加了一层Stack,其下第一个text就是网格框。
这里需要根据具体布局来计算下网格框大小。height 135后面的16是applyNormalAttribute带来的边框,7为5%的放大值。
List() {
ForEach(infoList, (info: ThirdPartyAppInfo) => {
ListItem() {
Stack() {
Text() // 此为网格框,保证focus放大效果不超出网格
.height(135 + 16 +7) // 7为135的5%
.width(241 + 16 + 12) // 12同上
.focusable(false)
Column() {
Stack() {
Text()
.height(135 + (this.isFocusItem[info.index] ? 7 : 0))
.width(241 + (this.isFocusItem[info.index] ? 12 : 0))
.borderRadius(24)
.backgroundColor('#' + info.mainColor)
Image('file://' + info.appIcon)
.height(67 + (this.isFocusItem[info.index] ? 7 : 0)) // 放大10%
.width(85 + (this.isFocusItem[info.index] ? 9 : 0))
.objectFit(ImageFit.ScaleDown)
}
.attributeModifier(this.isFocusItem[info.index] ? this.modifier : this.modifierNormal)
......
}
.id(info.appName)
.onFocus(() => {
focusControl.requestFocus(info.appName)
this.isFocusItem[this.lastFocusIndex] = false
this.isFocusItem[info.index] = true
this.lastFocusIndex = info.index
})
.onHover((isHover?: boolean) => {
if (isHover) {
focusControl.requestFocus(info.appName)
this.isFocusItem[this.lastFocusIndex] = false
this.isFocusItem[info.index] = true
this.lastFocusIndex = info.index
}
})
......
}
.margin({ right: 4, bottom: 22 })
}
})
}
视频效果:
2025-02-04-23-13-03-focus效果
更多推荐



所有评论(0)