大家好我是牛牛,一名软件开发从业者,无意中接触到了鸿蒙移动端开发,对鸿蒙操作系统产生了极大的兴趣,作者将从无到有开发出一款鸿蒙原生APP。每天写一篇关于鸿蒙开发的技术文章。欢迎大家踊跃订阅➕关注,文章中有什么不妥之处可以在评论区中指出。
注意:最好是有开发经验的伙伴来阅读系列文章。零基础的同学可以先去了解一下TypeScript从最基本的开发语言进行学起
前言
上一章节《一百天挑战学会HarmanyOS——组件的双向绑定与事件监听》介绍了组件的双向绑定,组件的状态管理,有了这些使得有着更好的开发体验,而在实际开发过程中,用法比上一章节案例中代码复杂的多,所以有兴趣的伙伴一定要去亲自尝试。有什么疑问的地方大家可以在评论区留言。
本章介绍
本章节主要介绍ArkUI中样式相关的知识点,在其他章节中的代码案例中也出现过样式的使用,但是没有深入的去讲解,包括其中还有框架所提供的定义组件重用样式,定义扩展组件样式,多态样式,这些内容将在这一章节中展开。
样式
样式-语法(链式调用&枚举)
ArkTs以声明方式组合和扩展组件来描述应用程序的 UI;同时还提供了基本的属性,事件和子组件的配置方法,帮助开发者实现应用的交互逻辑。
样式属性
- 属性方法以
.的链式调用的方式配置系统组件的样式和其他属性。
什么是链式调用,以下面的示例为例:
@Entry
@Component
struct StyleCasePage {
build() {
Column() {
Text('样式演示')
.fontSize(30)
.width('100%')
.textAlign(TextAlign.Center)
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
}
以上代码中Text组件后面使用的属性方法.fontSize(),.width(),.textAlign(), 都是给 Text 组件修饰样式的属性方法,使用.的链式调用的方式。
枚举
- 对于系统组件,ArkUI 还为其属性定义了一些枚举类型。详细见枚举文档链接。
@Entry
@Component
struct StyleCasePage {
build() {
Column() {
Text('样式演示')
.fontSize(30)
.width('100%')
// 使用枚举值进行布局的设置
.textAlign(TextAlign.Center)
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
}
以上代码中Text组件后面使用的.textAlign()属性方法里的参数值TextAlign.Center的TextAlign就是一个枚举类型,用来设置文本的对齐方式,示例代码中设置的为居中显示。查看源码得知TextAlign是一个枚举类型。
样式-单位(vp&fp)
官方定义
- 使用虚拟像素,使元素在不同密度的设备上具有一致的视觉体量。
vp 是什么?
英文名:virtual pixel
- 屏幕密度相关像素,根据屏幕像素密度转换为屏幕的物理像素,当数值不带单位时,默认单位使用
vp;在实际像素 1440 物理像素的屏幕上,1vp约等于3px物理像素。
其实在文章上面的案例代码中有使用过vp,注意 vp 与 px 单位显示效果的是不同的,下面代码作为演示。
使用 vp 为单位:
@Entry
@Component
struct StyleCasePage {
build() {
Column() {
Text('样式演示')
// 不书写单位默认使用 vp
.fontSize(30)
.width('100%')
.textAlign(TextAlign.Center)
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
使用 px 为单位:
@Entry
@Component
struct StyleCasePage {
build() {
Column() {
Text('样式演示')
// 使用 px 为单位
.fontSize('30px')
.width('100%')
.textAlign(TextAlign.Center)
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
}
fp是什么?
英文名: font pixel
字体像素(font pixel) 大小默认情况下与 vp 相同,即默认情况下 1 fp = 1vp。如果用户在设置中选择了更大的字体,字体的实际显示大小就会在 vp 的基础上乘以用户设置的缩放系数,即 1 fp = 1 vp * 缩放系数。
ArkUI为开发者提供4种像素单位,框架采用vp为基准数据单位。
提供其他单位与px单位互相转换的方法。
示例:
@Entry
@Component
struct Example {
build() {
Column() {
Flex({ wrap: FlexWrap.Wrap }) {
Column() {
Text("width(220)")
.width(220)
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12vp')
}.margin(5)
Column() {
Text("width('220px')")
.width('220px')
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
}.margin(5)
Column() {
Text("width('220vp')")
.width('220vp')
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12vp')
}.margin(5)
Column() {
Text("width('220lpx') designWidth:720")
.width('220lpx')
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12vp')
}.margin(5)
Column() {
Text("width(vp2px(220) + 'px')")
.width(vp2px(220) + 'px')
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12vp')
}.margin(5)
Column() {
Text("fontSize('12fp')")
.width(220)
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12fp')
}.margin(5)
Column() {
Text("width(px2vp(220))")
.width(px2vp(220))
.height(40)
.backgroundColor(0xF9CF93)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.fontSize('12fp')
}.margin(5)
}.width('100%')
}
}
}
在样式中,我们如果写px,那么px直接表示的是物理像素,也就是分辨率,那么我们的手机分辨率密度各有不同,无法针对这种密度写一个固定值,所以vp会自动根据手机密度去进行适配,所以vp它提供了一种灵活的方式来适应不同屏幕密度的显示效果。
在不同屏幕物理分辨率下,要想实现等比例适配, 可以吗?
如下图:
-
设置lpx基准值 - resources/base/profile/main-pages.json
-
添加window属性,设置desigWidth,不设置也可以使用lpx,默认720
@Entry
@Component
struct PXCase {
build() {
Row() {
Column() {
Text('375lpx')
.width('375lpx')
.height('72lpx')
.textAlign(TextAlign.Center)
.backgroundColor(Color.Red)
Divider().strokeWidth(2)
Row(){
Text('72lpx')
}
.width('72lpx')
.height('25lpx')
.backgroundColor(Color.Brown)
}
.width('100%')
}
.height('100%')
}
}
伸缩布局方案
layoutWeight
有三种方式提供选择:
- 设定基准值,使用lpx,类似于前端的rem
- 监听元素的变化-可以拿到宽高-重新计算
- layoutWeight(number)- 剩余资源再分配
下面介绍一下使用 layoutWeight的方式
伸缩 layoutWeight(flex: number) 占剩余空间多少份,可以理解成CSS的 flex: 1
示例:
我们可以使用layoutWeight属性,让右侧内容去占满剩余宽度
代码:
@Entry
@Component
struct LayoutCasePage {
build() {
Row() {
Text("左侧内容")
Text("右侧内容")
.textAlign(TextAlign.End)
.width('80%')
.height(60)
.backgroundColor('red')
.layoutWeight(1)
}.width('100%')
.height('100%')
}
}
再如下图:
当我们使用layoutWeight属性进行剩余空间布局的时候,我让绿色部分占了剩余空间的 5份,橘黄色部分占用了剩余空间的1份
@Entry
@Component
struct LayoutCasePage {
build() {
Column() {
Row() {
}
.width('100%')
.height(50)
.backgroundColor(Color.Blue)
Column() {
}
.width('100%')
.backgroundColor(Color.Green)
// 其中绿色占 5 份
.layoutWeight(5)
Column() {
}
.width('100%')
.backgroundColor(Color.Orange)
// 橘黄色占 1 份
.layoutWeight(1)
Row() {
}
.width('100%')
.height(50)
.backgroundColor(Color.Red)
}
.height("100%")
.width("100%")
.justifyContent(FlexAlign.SpaceBetween)
}
}
设置元素的宽高比
使用aspectRatio属性方法设置元素的宽高比,使得在不同的大小的设备上,元素的宽高比会等比缩放。元素不会在不同设备上显示形状不统一的情况。
如下图:
@Entry
@Component
struct LayoutCasePage {
build() {
Column()
.width('50%')
.height('50%')
.backgroundColor('blue')
.aspectRatio(1)
}
}
样式复用@Styles
在开发过程中会出现大量代码在进行重复样式设置,@Styles装饰器可以帮我们进行样式复用
注意:
@Styles和@Extend均只支持在当前文件下的全局或者组件内部定义,如果你想要在其他文件导出一个公共样式,导出公共使用,ArtTS是不支持的,这种方式还是需要考虑组件复用。- 在
@Styles修饰的函数中能够点出来就是通用属性和事件-Text的字体颜色-字体大小不属于通用属性 @Styles修饰的函数不允许传参数- 全局
@Styles不支持箭头函数语法 - 全局
@Styles扩展符只能和使用它的组件位于同一个文件,不允许导入导出,导入导出也使用不了
下面使用示例为大家演示一下
假如我们要实现下面的功能,页面中有三个按钮,这个三个按钮的颜色,样式,点击事件都是一样的,如果不用样式复用代码是什么样子的呢?
效果图:
不使用样式复用代码实现
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct StylePage {
build() {
Column({ space: 20 }) {
Row() {
Button("微信支付", { type: ButtonType.Normal })
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor("#00c168")
.onClick(() => {
promptAction.showToast({ message: '支付宝支付成功' })
})
.fontColor(Color.White)
}
.padding(10)
Row() {
Button("微信支付", { type: ButtonType.Normal })
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor("#00c168")
.onClick(() => {
promptAction.showToast({ message: '支付宝支付成功' })
})
.fontColor(Color.White)
}
.padding(10)
Row() {
Button("微信支付", { type: ButtonType.Normal })
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor("#00c168")
.onClick(() => {
promptAction.showToast({ message: '支付宝支付成功' })
})
.fontColor(Color.White)
}
.padding(10)
}
}
}
我们发现在以上代码中每个Button 组件中使用的样式属性都是统一的,如果页面有 10 个按钮呢?我们需要些 10遍吗?
使用@Styles的方式进行样式的复用代码实现
我们把代码中每个Button 组件中使用的样式属性抽成一个方法,用@Styles 进行装饰,然后在代码中调用这个方法即可。
@Styles
payStyle () {
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor("#00c168")
.onClick(() => {
promptAction.showToast({ message: '微信支付成功' })
})
}
完整示例
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct StylePage {
@Styles
payStyle () {
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor("#00c168")
.onClick(() => {
promptAction.showToast({ message: '微信支付成功' })
})
}
build() {
Column({ space: 20 }) {
Row() {
Button("微信支付", { type: ButtonType.Normal })
.payStyle()
.fontColor(Color.White)
}
.padding(10)
Row() {
Button("微信支付", { type: ButtonType.Normal })
.payStyle()
.fontColor(Color.White)
}
.padding(10)
Row() {
Button("微信支付", { type: ButtonType.Normal })
.payStyle()
.fontColor(Color.White)
}
.padding(10)
}
}
}
样式复用@Extend
假设我们就想针对Button进行字体和样式的复用,此时可以使用@Extend来修饰一个全局的方法
注意:
- 使用
@Extend装饰器修饰的函数只能是 全局 - 函数可以进行 传参,如果参数是状态变量,状态更新后会刷新UI
- 且参数可以是一个函数,实现复用事件且可处理不同逻辑
@Extend扩展符只能和使用它的组件位于同一个文件,不允许导入导出,导入导出也使用不了
假如我们有这样一个需求,根据不同类型设置不同按钮的样式与点击事件。这个时候使用@Styles 就满足不了这个场景啦。下面使用@Extend来实现。
示例:
代码:
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct ExtendCase {
build() {
Column({ space: 20 }) {
Button("微信支付")
.payButton("alipay")
Button("微信支付")
.payButton("wechat")
Button("微信支付")
.payButton("alipay")
Button("微信支付")
.payButton("wechat")
Button("微信支付")
.payButton("alipay")
Button("微信支付")
.payButton("wechat")
Button("微信支付")
.payButton("alipay")
}
.padding(20)
.width('100%')
}
}
// 不允许导出
@Extend(Button)
function payButton (type: "alipay" | "wechat") {
.type(ButtonType.Normal)
.fontColor(Color.White)
.width('100%')
.height(50)
.borderRadius(4)
.backgroundColor(type === "wechat" ? "#00c168" : "#ff1256e0")
.onClick(() => {
if(type === "alipay") {
promptAction.showToast({ message: '支付宝支付成功' })
}else {
promptAction.showToast({ message: '微信支付成功' })
}
})
}
多态样式stateStyles
@Styles和@Extend仅仅应用于静态页面的样式复用,stateStyles可以依据组件的内部状态的不同,快速设置不同样式。这就是我们要介绍的内容stateStyles(又称为:多态样式)。
ArkUI 提供以下五种状态:
- focused:获焦态。
- normal:正常态。
- pressed:按压态。
- disabled:不可用态。
- selected: 选中态
假设我们想做一个微信中点击的选中状态, 如图
该图在点击时会有变色,抬起时消失,此时就可以利用多态样式进行设置
代码:
@Entry
@Component
struct StateStylesCase {
build() {
Column({ space: 20 }) {
Row() {
Text("你今天想我了吗")
}
.padding(20)
.height(80)
.border({
color: '#f3f4f5',
width: 3
})
.borderRadius(4)
// 多态样式
.stateStyles({
// 正常态
normal: {
.backgroundColor(Color.White)
},
pressed: {
.backgroundColor("#eee")
}
})
.width('100%')
}
.padding(20)
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
这一期就到这里啦!为了方便大家我这一期代码放到git仓库上,建议大家动起手实操起来。 项目代码git