样式处理
1. 样式-语法(链式&枚举)
ArkTS以声明方式组合和扩展组件来描述应用程序的UI 同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。
1.样式属性
- 属性方法以
.
链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。
java
@Entry
@Component
struct Index {
build() {
Text('演示')
.backgroundColor('red')
.fontSize(50)
.width('100%')
.height(100)
}
}
2.枚举值
- 对于系统组件,ArkUI还为其属性预定义了一些枚举类型。
java
@Entry
@Component
struct Index {
build() {
Text('演示')
.fontSize(50)
.width('100%')
.height(100)
.backgroundColor(Color.Blue)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
}
}
- 样式相关属性通过链式函数的方式进行设置
- 如果类型是枚举的,通过枚举传入对应的值
2. 样式-单位 vp
和适配
vp
是什么?virtual pixel
-
屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位
vp
;在实际宽度为1440物理像素的屏幕上,1vp
约等于3px
(物理像素) -
上图的意思是,使用这个单位在不同屏幕物理分辨率的实际尺寸一致(A设备1英寸,B设备1英寸)。
2.之前 vw
、rem
和 rpx
相对于屏幕宽度的单位,可以实现等比例适配,vp
可以吗?
java
import promptAction from '@ohos.promptAction'
@Entry
@Component
struct Index {
build() {
Text('演示')
.width('100%')
.backgroundColor('red')
.onAreaChange((oldArea, newArea) => {
promptAction.showToast({
// 1. onAreaChange改变尺寸后会触发
// 2. newArea为现在元素尺寸
message: newArea.width.toString()
})
})
}
}
我们发现:不同的设备屏幕的宽度 vp
是不一致的,那怎么适配呢?
- 根据官方的文档,结合自己的理解,采用:伸缩布局,网格系统,栅格系统进行布局适配。
伸缩 layoutWeight(flex: number)
占剩余空间多少份,可以理解成CSS的 flex: 1
java
@Entry
@Component
struct Index {
build() {
Row(){
Text('left')
.layoutWeight(1)
.backgroundColor('red')
Text('right')
.layoutWeight(2)
.backgroundColor('green')
}
.width('100%')
}
}
等比例,设置元素宽高比 aspectRatio(ratio: number)
java
@Entry
@Component
struct Index {
build() {
Text('left')
.width('50%')
// 宽高比例
.aspectRatio(1)
.backgroundColor('red')
}
}
vp
是鸿蒙默认单位,和屏幕像素有关,最终表现视觉大小在任何设备一致- 鸿蒙一般以伸缩
layoutWeight
、网格、栅格进行布局适配,如要等比例缩放可以设置高宽比aspectRatio
📕📕📕 案例→实现知乎评论回复-评论区域
设计稿一般是1080px:(这里没有设计稿,提供了一些尺寸)
- Nav
- 左侧返回按钮
24vp
高宽背景颜色#f5f5f5
,图标12vp尺寸颜色#848484
- 标题
18vp
- 左侧返回按钮
- Comment
- 头像尺寸32vp高宽,右侧间距10vp
- 标题15vp,颜色默认
- 内容16vp,颜色
#565656
- 底部12vp,颜色
#c3c4c5
java
@Entry
@Component
struct Index {
build() {
Column(){
// 导航
Row(){
Row(){
Image($r('app.media.ic_public_arrow_left'))
.width(16)
.aspectRatio(1)
// svg 图标可以使用填充颜色
// .fillColor('red')
}
.width(24)
.aspectRatio(1)
.backgroundColor('#f5f5f5')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.margin({ left: 16 })
Text('评论回复')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ right: 40 })
}
.height(40)
.border({ width: { bottom: 0.5 }, color: '#e4e4e4' })
// 评论
Row(){
Image($r('app.media.avatar'))
.width(32)
.aspectRatio(1)
.borderRadius(16)
Column({ space: 5 }){
Text('周杰伦')
.width('100%')
.fontWeight(FontWeight.Bold)
.fontSize(15)
Text('大理石能雕刻出肌肉和皮肤的质感,那个年代的工匠好牛啊')
.width('100%')
Row(){
Text('10-21 · IP属地北京')
.fontSize(12)
.fontColor('#c3c4c5')
Row({ space: 4 }){
Image($r('app.media.ic_public_heart'))
.width(14)
.aspectRatio(1)
.fillColor('#c3c4c5')
Text('100')
.fontSize(12)
.fontColor('#c3c4c5')
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.layoutWeight(1)
.padding({ left: 10 })
}
.padding(15)
.alignItems(VerticalAlign.Top)
}
}
}
样式-@Styles 复用
在开发过程中会出现大量代码在进行重复样式设置,@Styles 可以帮我们进行样式复用
- 当前
@Styles
仅支持 通用属性 和 通用事件。 - 支持 全局 定义和 组件内 定义,同时存在组件内覆盖全局生效。
java
// 全局
@Styles
function functionName() { ... }
@Entry
@Component
sturt Index{
// 组件内
@Styles
functionName() { ... }
build() {
Text('Text')
.functionName()
}
}
案例:文字和按钮相同背景,点击+1
- 全局
java
@Styles function sameStyle() {
.backgroundColor(Color.Green)
.onClick(() => {
this.count++
})
}
@Entry
@Component
struct Index {
@State
count: number = 10
build() {
Column() {
Text(this.count.toString())
.width(100)
.height(50)
.margin({ bottom: 10 })
.textAlign(TextAlign.Center)
.sameStyle()
Button('+1')
.sameStyle()
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
- 组件内
java
@Entry
@Component
struct Index {
@State
count: number = 10
// 不需要 `function` 关键字,覆盖全局
@Styles
sameStyle (){
.backgroundColor(Color.Pink)
.onClick(() => {
this.count += 10
})
}
build() {
Column() {
Text(this.count.toString())
.width(100)
.height(50)
.margin({ bottom: 10 })
.textAlign(TextAlign.Center)
.sameStyle()
Button('+1')
.sameStyle()
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
📕📕📕 练习案例-登录表单-样式优化
java
import promptAction from '@ohos.promptAction'
@Entry
@Component
struct Index {
@State
mobile: string = ''
@State
code: string = ''
@Styles
inputStyle () {
.border({ width: 1, color: Color.Gray })
.layoutWeight(1)
.margin({ left: 10, bottom: 10, top: 10 })
.backgroundColor(Color.White)
}
build() {
Column(){
Row(){
Text('手机号')
TextInput({ text: this.mobile })
.inputStyle()
.onChange((value)=>this.mobile = value)
}
Row(){
Text('验证码')
TextInput({ text: this.code })
.inputStyle()
.onChange((value)=>this.code = value)
}
Row({ space: 15 }){
Button('重置')
.backgroundColor('#ccc')
.onClick(()=>{
this.mobile = ''
this.code = ''
})
Button('登录')
.onClick(()=>{
if (this.mobile && this.code) {
promptAction.showToast({ message: `${this.mobile} 登录成功` })
} else {
promptAction.showToast({ message: `请输入手机号或验证码` })
}
})
}
}
.padding({ left: 15, right: 15 })
}
}
样式-@Extends 复用
@Extend 用于扩展原生组件样式,通过传参提供更灵活的样式复用
- 使用
@Extend
装饰器修饰的函数只能是全局
- 函数可以进行
传参
,如果参数是状态变量,状态更新后会刷新UI - 且参数可以是一个函数,实现复用事件且可处理不同逻辑
java
// 全局 原生组件 参数
// ↓ ↓ ↓
@Extend(Text) function functionName(w: number) {
.width(w)
}
需求:把 Text 改成按钮样式,且绑定 click 事件执行不同逻辑
java
import promptAction from '@ohos.promptAction'
@Extend(Text) function myClick(color: string, cb: () => void) {
.backgroundColor(color)
.width(100)
.height(50)
.textAlign(TextAlign.Center)
.borderRadius(25)
.onClick(() => cb())
}
@Entry
@Component
struct Other {
@State
color: string = '#ccc'
build() {
Column({ space: 20 }) {
Text('Text1')
.myClick(this.color, () => {
this.color = '#069'
})
Text('Text2')
.myClick('green', () => {
promptAction.showToast({ message: '做其他事~' })
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
样式-多态
stateStyles
是属性方法,可以根据UI内部状态来设置样式,类似于 css 伪类,但语法不同。ArkUI 提供以下四种状态:
-
focused:获焦态。
-
normal:正常态。
-
pressed:按压态。
-
disabled:不可用态。
java
import promptAction from '@ohos.promptAction'
// 胶囊按钮
@Extend(Text)
function capsule(){
.height(40)
.borderRadius(20)
.backgroundColor(Color.Gray)
.padding({ left: 15, right: 15 })
.margin({ bottom: 15 })
}
@Entry
@Component
struct Index {
@State
disabled: boolean = false
@State
focused: boolean = false
build() {
Column() {
// Button TextInput 默认开启获取焦点,页面中默认第一个这样的元素获取焦点
// Button 比较多限制,一个是默认开启获取焦点能看,二是禁用状态下样式无法修改
// Button('Button').focusable(false)
Text('toggle disabled:' + this.disabled)
.capsule()
.onClick(()=>{
this.disabled = !this.disabled
})
Text('toggle focused:' + this.focused)
.capsule()
.onClick(()=>{
this.focused = !this.focused
})
Text('clickMe')
.capsule()
.enabled(!this.disabled)
.focusable(this.focused)
.onClick(() => {
promptAction.showToast({ message: 'click' })
})
.fontColor('#fff')
.stateStyles({
normal: {
.backgroundColor(Color.Blue)
},
focused: {
.backgroundColor(Color.Red)
},
disabled: {
.backgroundColor(Color.Black)
},
pressed: {
.backgroundColor(Color.Orange)
}
})
}
}
}
- 使用比较多的应该是
norma
pressed
结合下的按压效果 enabled(true|false)
开启|禁用focusable(true|false)
开启获取焦点能力|关闭
注意:
- 页面初始化的时候,默认第一个能获取焦点的元素,会自动获取焦点
👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀