Demo1:图片缩放案例
实现图:
代码:
@Entry
@Component
struct Index {
@State imageWidth: number = 200
build() {
Column() {
Row(){
Image($r('app.media.icon'))
.width(this.imageWidth)
.interpolation(ImageInterpolation.High)
}
.width("100%")
.height(400)
.justifyContent(FlexAlign.Center)
Row(){
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
TextInput({text:this.imageWidth.toFixed(0)})
.width(200)
.backgroundColor('#fff')
.type(InputType.Number)
.onChange((value)=>{
this.imageWidth = parseInt(value)
})
}
// .padding(20)
.padding({left:20,right:20})
.width("100%")
.justifyContent(FlexAlign.SpaceBetween)
Divider()
.width("90%")
Row(){
Button('缩小')
.width(80)
.fontSize(20)
.onClick(()=>{
if(this.imageWidth>=80){
this.imageWidth -= 10
}else{
AlertDialog.show(
{
title: '警告',
message: '不可再减小',
})
}
})
Button('增大')
.width(80)
.fontSize(20)
.onClick(()=>{
if(this.imageWidth<=300){
this.imageWidth += 10
}else{
AlertDialog.show(
{
title: '警告',
message: '不可再增加',
})
}
})
}
.width("100%")
.margin({top:20})
.justifyContent(FlexAlign.SpaceEvenly)
Slider({
min:100,
max:300,
value:this.imageWidth,
step:10
})
.width('100%')
.blockColor('#36d')
.margin({top:10})
.trackThickness(7)
.showTips(true)
.onChange((value)=>{
this.imageWidth=value
})
}
.width('100%')
.height('100%')
}
}
本案例为基础入门案例,非常简单。实现了通过输入宽度、按钮、拖拉条三种方式来调节图片的大小。所用组件、方法、布局如下:
布局:
Column和Row容器相结合
Column容器
Column是沿垂直方向布局的容器
Row容器
Row是沿水平方向布局的容器
设置主轴上的排列可以用justifyContent,与CSS中的使用基本一致,有start(从行首开始排列),left(对齐左边缘排列),SpaceEvenly(均匀排列每个元素,元组件间距相等),Center(居中排列)等多种属性可以选择。
Center SpaceEvenly
组件:
Image组件
Image组件可以加载本地图片和网络图片
加载本地图片时可以通过Image($r(app.media.图片名))的形式,($r方法用于读取base目录下的media目录中的图片)或者以Image($rawfile('图片名.图片格式))来实现。
加载网络图片则可以通过Image('https://xxx.png')的格式来实现,但需要注意的是,使用模拟器显示图片时,需要在module.json5配置文件中配置权限
如上图,在requestPermissions中配置网络权限,则可以使用网络图片。
(具体参考访问控制授权申请-访问控制-安全-开发 | 华为开发者联盟 (huawei.com))
Text组件
text组件是用于显示文本的组件
最简单的如Text('hello world')这种直接在组件中填入字符串的方式
或者通过resource格式,读取本地的资源文件夹resources中的限定词目录en_US(英文)和zh_CN(中文)
在en_US(英文)的string.json中添加: 在zh_CN(中文)的string.json中添加
然后在代码中以Text($r('app.string.width_label))来读取value值,系统会根据环境渲染出对应的value值
如当前为zh-CN,则渲染出来的值为“图片宽度”。
这里有个需要注意的点!
除了在上述两个文件夹的string.json中添加外,我们还需要再base的element下的string.json添加一次,value值为中文或者英文都可,因为如果编译器在限定词目录中找不到你所定义的参数的话,他会回到base下寻找,如果没有在base下添加的话,会发生报错。
TextInput组件
这是一个单行文本输入框组件
可以通过设置placeholder的值来设置用户为输入之前的提示文本
或者设置text属性来设置文本行中的初始值
Button组件
按钮组件,可快速创建不同样式的按钮。
常与点击事件onClick方法结合使用,点击后实现某些逻辑操作。
可以通过type属性控制属性样式,Capsule为胶囊型,Circle为圆形(可以结合icon实现图标按钮),Normal为长方形。
同时可以设置stateEffect的boolean值来控制奠基石是否出现按压效果。
Button({type:ButtonType.Capsule,stateEffect:true})
Slider组件
滑动条组件,通常用于快速调节设置值。
通常需要设置min(最小值),max(最大值),value(初始值),step(步长,即每移动一次增加或者减少多少)。
方法:
onChange方法
当方法中的值发生改变时会触发调用,常用于滚动事件、输入事件等,如上述的TextInput组件和Slider组件中就绑定了onChange方法来检测值的变化。
.onChange((value)=>{
this.imageWidth=value
})
如上述代码实现了将滑动条的值赋值给图片的宽度,实现调节图片大小的功能。
onClick方法
当方法绑定的组件被点击时会触发调用,常用于按钮组件,图片点击出现大图等,如上述点击按钮实现增大缩小图片的功能就有所体现。
Demo2:商品列表案例
实现图:
index代码:
class Item {
name: string
image: ResourceStr
price: number
discount: number
constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}
import {Header} from '../component/TitleHeader'
//全局自定义构建函数
// @Builder function ItemCard(item:Item){
// Row({space:10}){
// Image(item.image)
// .width(100)
// Column({space:4}){
// Text(item.name)
// .fontSize(20)
// .fontWeight(FontWeight.Bold)
// if(item.discount){
// Text('原价:¥'+item.price)
// .fontColor('#CCC')
// .fontSize(14)
// .decoration({type:TextDecorationType.LineThrough})
// Text('折扣价:¥'+(item.price-item.discount))
// .fontColor('#F36')
// .fontSize(18)
// Text('补贴:¥'+item.discount)
// .fontColor('#F36')
// .fontSize(18)
// }else{
// Text('¥'+item.price)
// .fontColor('#F36')
// .fontSize(18)
// }
// }
// .height('100%')
// .alignItems(HorizontalAlign.Start)
// }
// .width('100%')
// .backgroundColor('#FFF')
// .borderRadius(20)
// .height(120)
// .padding(10)
// }
//全局公共样式
// @Styles function fillScreen(){
// .width('100%')
// .backgroundColor('#EFEFEF')
// .height("100%")
// .padding(20)
// }
//不是全局通用样式,所以要用Extend()继承装饰器
@Extend(Text) function priceText(){
.fontColor('#F36')
.fontSize(18)
}
@Entry
@Component
struct ItemPage {
// 商品数据
private items: Array<Item> = [
new Item('华为Mate60', $r('app.media.mate60'),6999, 500),
new Item('MateBookProX', $r('app.media.mateBookProX'),13999),
new Item('WatchGT4', $r('app.media.watchGT4'),1438),
new Item('FreeBuds Pro3', $r('app.media.freeBudsPro3'),1499),
new Item('Mate X5', $r('app.media.mateX5'),12999)
]
build() {
Column({space: 8}){
Header({title:'商品页面'})
.margin({top:20,bottom:10})
List({space:8}){
ForEach(
this.items,
(item: Item)=>{
ListItem(){
this.ItemCard(item)
}
}
)
}
.width('100%')
.layoutWeight(1)
}
.fillScreen()
}
//局部自定义构建函数
@Builder ItemCard(item:Item){
Row({space:10}){
Image(item.image)
.width(100)
Column({space:4}){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
if(item.discount){
Text('原价:¥'+item.price)
.fontColor('#CCC')
.fontSize(14)
.decoration({type:TextDecorationType.LineThrough})
Text('折扣价:¥'+(item.price-item.discount))
.priceText()
Text('补贴:¥'+item.discount)
.priceText()
}else{
Text('¥'+item.price)
.priceText()
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#FFF')
.borderRadius(20)
.height(120)
.padding(10)
}
//局部公共样式
@Styles fillScreen(){
.width('100%')
.backgroundColor('#EFEFEF')
.height("100%")
.padding(20)
}
}
TitleHeader代码:
@Component
export struct Header{
private title:ResourceStr
build(){
Row(){
Image($r('app.media.back'))
.width(30)
Text(this.title)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Blank()//空白组件
Image($r('app.media.return'))
.width(30)
}
.width("100%")
.height(30)
}
}
这个案例是我们非常熟悉的手机商品列表页,也是非常基础的一个案例。
这个案例中涉及到的大部分组件、布局等前面案例中已经讲过,就不再赘述了。我介绍一些前面案例中所没有涉及的内容。
组件:
Blank组件
空白填充组件,在容器主轴方向上,空白填充组件具有自动填充容器空余部分的能力。仅当父组件为Row/Column/Flex时生效。
如这个页头,又三部分组成,返回符号,商品页面文字,刷新符号,我们如果使用space,他将会让三个部分之间两两间隔,无法实现我们想要的效果。那么我们就可以在文字与刷新符号之间添加一个Blank组件,使其占满两者之间的空余,把刷新符号“挤”到右边去。
自定义组件
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。
自定义组件的特点:可组合、可重用、数据驱动UI更新。
@Component
struct Header{
build(){
Row(){...}
.width("100%")
.height(30)
}
}
自定义组件的基本结构:首先要有@Component装饰器,@Component装饰器仅能装饰struct关键字声明的数据结构,而自定义组件基于struct实现,build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数,三者缺一不可,所以基本结构如上图代码。
@Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件。在@Entry装饰的组件中,可以调用其他自定义组件。换句话说,我们可以在@Entry中一些重复用到的代码封装为一个自定义组件,以达到复用的效果。
若这些自定义组件不止在一个UI页面中被使用到,我们则可以将其封装为单独的ets文件,存放在Components文件夹中,同时我们需要再struct前加上export关键字,将此方法导出,然后使用import关键字进行引入,如:
import {Header} from '../component/TitleHeader'
这样我们就可以在多个UI页面中使用到我们封装出的自定义组件了。
自定义构建函数
自定义构建函数的装饰器为@Builder,@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。自定义构建函数分为全局自定义构建函数,局部自定义构建函数。类比全局变量和局部变量我们可知,两者的差别在于其可使用的范围。
全局自定义构建函数:
@Builder function ItemCard(item:Item){
Row({space:10}){...}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#FFF')
.borderRadius(20)
.height(120)
.padding(10)
}
如上述代码所示,@Builder function 函数名(参数){函数内容},这就是全局自定义构建函数的基本形式。函数内容和在@Component编写代码是相同的,就是将一段代码封装进函数中。同时因为是全局函数,所以不可以定义在组件内,应该在@Component之外的位置进行定义,使用也非常简单,和其他内置的组件一样使用就可以。如上述自定义函数,则在需要的地方添加ItemCard(item)即可。
局部自定义构建函数
@Builder ItemCard(item:Item){
Row({space:10}){...}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#FFF')
.borderRadius(20)
.height(120)
.padding(10)
局部自定义构建函数和全局自定义构建函数的编写方式非常相似,但是局部构建函数不需要添加function关键字,且应定义在要使用到这个构建函数的@Component之中,其余地方与全局自定义构建函数无异。
自定义构建函数和自定义组件都可以做到复用代码的目的,提高了代码的复用性,但是二者也存在着一定的差别。
- 自定义构建函数(@Builder)更轻量,其作为UI元素抽象的方法,实现和调用相较于自定义组件比较简洁。
- 在自定义组件中,可以定义成员函数/变量、自定义组件生命周期等。
- 而自定义构建函数(@Builder)不支持定义状态变量和自定义生命周期。
- 在自定义组件中,可直接通过状态变量的改变,来驱动UI的刷新。
- 而自定义构建函数(@Builder)默认的按值参数传递方式不支持动态改变组件,当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新,要实现UI动态刷新需• 要按引用传递参数。
- 在自定义组件中要实现插槽功能,需要使用@Builder和@BuilderParam实现。
- 具体实现可参考:@BuilderParam装饰器:引用@Builder函数。
- 自定义构建函数(@Builder)中使用了自定义组件,那么该方法每次被调用时,对应的自定义组件均会重新创建。
(来自论坛一位网友的总结)
公共样式:
同样,公共样式也分全局公共样式和局部公共样式,类比上述的全局自定义构建函数和局部自定义构建函数。公共样式的装饰器是@Styles,@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。
但是需要注意的是,@Styles装饰器下的样式,需要是组件通用样式,如:width, backgroundcolor等,如果出现类似fontsize,fontcolor时,编译器将会报错。
那是否意味着这些样式就不可封装呢,也不是,这时候我们需要用@Extend装饰器。@Extend是在@Style的基础上,用于扩展原生组件样式的。@Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的@Extend的方法。也就是说,你需要给他一个预定义的组件。如要封装fontsize,fontcolor时,需要预定义组件为Text,具体使用方法如下:
@Extend(Text) function priceText(){
.fontColor('#F36')
.fontSize(18)
}
同时也要注意,@Extend仅支持定义在全局,不支持在组件内部定义。
If I never see you again, good morning, good afternoon and good night. ——《楚门的世界》