前言
目前HarmonyOS ArkUI 3.0框架的容器组件共有21个,在学习完这21个容器组件后,打算使用尽可能多的容器组件基于HarmonyOS ArkUI 3.0框架去完成一个实践开发,一个实践项目检验容器组件的学习成果就来了o( ̄︶ ̄)o
本次实践项目涉及的容器组件有:Badge(新事件标记组件)、Column(沿垂直方向布局的容器)、Flex(弹性布局组件)、List(列表包含一系列相同宽度的列表项)、ListItem(用来展示列表具体item)、Navigator(路由容器组件)、Row(沿水平方向布局容器)、Scroll(可滚动的容器组件)、Swiper(滑动容器)、Tabs(一种可以通过页签进行内容视图切换的容器组件)、TabContent(对应一个切换页签的内容视图)。
效果图
欢迎页面线性渐变角度添加了渐变径向渐变
代码文件结构
正文
一、创建一个空白工程
1. 安装和配置DevEco Studio 3.0
2. 创建一个Empty eTS Ability应用
DevEco Studio下载安装成功后,打开DevEco Studio,点击左上角的File,点击New,再选择New Project,选择Empty Ability选项,点击Next按钮。
将文件命名为GradientRamp(文件名不能出现中文或者特殊字符,否则将无法成功创建项目文件),Project Type勾选Application,选择保存路径,Language勾选eTS,选择API7,设备勾选Phone,最后点击Finish按钮。
3. 准备工作
在entry>src>main>config.json文件中最下方"launchType": "standard"的后面添加以下代码,这样就可以实现去掉应用上方的标签栏了。
config.json最下方部分代码:
- "metaData": {
- "customizeData": [
- {
- "name": "hwc-theme",
- "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar",
- "extra": ""
- }
- ]
- }
二、 欢迎页面
1. 添加背景
Column(沿垂直方向布局的容器)、Flex(弹性布局组件)、Row(沿水平方向布局容器)是最常见的三种容器组件,所以直接说明。
Flex
Flex:弹性布局组件
参数:
direction:非必填,子组件在Flex容器上排列的方向,即主轴的方向
- FlexDirection.Row:主轴与行方向一致作为布局模式(默认)
- FlexDirection.Row:与Row方向相反方向进行布局
- FlexDirection.RowReverse:主轴与列方向一致作为布局模式
- FlexDirection.ColumnReverse:与Column相反方向进行布局
wrap:非必填,Flex容器是单行/列还是多行/列排列
- FlexWrap.NoWrap:Flex容器的元素单行/列布局,子项允许超出容器(默认)
- FlexWrap.Wrap:Flex容器的元素多行/列排布,子项允许超出容器
- FlexWrap.WrapReverse:Flex容器的元素反向多行/列排布,子项允许超出容器
justifyContent:非必填,子组件在Flex容器上排列的方向,即主轴的方向子组件在Flex容器主轴上的对齐格式
- FlexAlign.Start:元素在主轴方向首端对齐, 第一个元素与行首对齐,同时后续的元素与前一个对齐(默认)
- FlexAlign.Center:元素在主轴方向中心对齐,第一个元素与行首的距离与最后一个元素与行尾距离相同
- FlexAlign.End:元素在主轴方向尾部对齐, 最后一个元素与行尾对齐,其他元素与后一个对齐
- FlexAlign.SpaceBetween:Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐
- FlexAlign.SpaceAround:Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素到行首的距离和最后一个元素到行尾的距离时相邻元素之间距离的一半
- FlexAlign.SpaceEvenly:Flex主轴方向元素等间距布局, 相邻元素之间的间距、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样
alignItems:非必填,子组件在Flex容器交叉轴上的对齐格式
- ItemAlign.Auto:使用Flex容器中默认配置
- ItemAlign.Start:元素在Flex容器中,交叉轴方向首部对齐
- ItemAlign.Center:元素在Flex容器中,交叉轴方向居中对齐
- ItemAlign.End:元素在Flex容器中,交叉轴方向底部对齐
- ItemAlign.Stretch:元素在Flex容器中,交叉轴方向拉伸填充,在未设置尺寸时,拉伸到容器尺寸(默认)
- ItemAlign.Baseline:元素在Flex容器中,交叉轴方向文本基线对齐
alignContent:非必填,交叉轴中有额外的空间时,多行内容的对齐方式。仅在wrap为Wrap或WrapReverse下生效
- FlexAlign.Start:元素在主轴方向首端对齐, 第一个元素与行首对齐,同时后续的元素与前一个对齐(默认)
- FlexAlign.Center:元素在主轴方向中心对齐,第一个元素与行首的距离与最后一个元素与行尾距离相同
- FlexAlign.End:元素在主轴方向尾部对齐, 最后一个元素与行尾对齐,其他元素与后一个对齐
- FlexAlign.SpaceBetween:Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐
- FlexAlign.SpaceAround:Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素到行首的距离和最后一个元素到行尾的距离时相邻元素之间距离的一半
- FlexAlign.SpaceEvenly:Flex主轴方向元素等间距布局, 相邻元素之间的间距、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样
Column
Column:沿垂直方向布局的容器
参数:
space:非必填,纵向布局元素间距,参数类型为Length,即直接填数字
属性:
alignItems:设置子组件在水平方向上的对齐格式
- HorizontalAlign.Start:按照语言方向起始端对齐
- HorizontalAlign.Center:居中对齐,默认对齐方式(默认)
- HorizontalAlign.End:按照语言方向末端对齐
Row
Row:沿水平方向布局容器
参数:
space:非必填,横向布局元素间距,参数类型为Length,即直接填数字
属性:
alignItems:在垂直方向上子组件的对齐格式
- VerticalAlign.Top:顶部对齐
- VerticalAlign.Center:居中对齐,默认对齐方式(默认)
- VerticalAlign.Bottom:底部对齐
在index.ets文件中,通过Text(‘渐变色盘’)和Text(‘一个懂你的调色盘’)可放置文字内容。
属性linearGradient为设置线性渐变颜色,linearGradient中的angle为渐变角度,设置为180,即为从上往下渐变,colors则为渐变的颜色。
要值得说明的是,如果文本属性添加了fontFamily(‘华文行楷’)的话,在预览器是能看到效果的,但在远程模拟器是看不到效果的,因为远程模拟器内是没有这个字体的。
index.ets:
- @Entry
- @Component
- struct Index {
- build() {
- Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
- Column(){
- Text('渐变色盘')
- .fontColor('#cf6cc9')
- .fontSize(60)
- .fontStyle(FontStyle.Italic)
- .fontWeight(700)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- Text('一个懂你的调色盘')
- .fontColor('#ee609c')
- .fontSize(40)
- .fontStyle(FontStyle.Italic)
- .fontWeight(600)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- .margin({ top: -5 })
- }
- }
- .width('100%')
- .height('100%')
- .linearGradient({
- angle: 120,
- colors: [['#d9ded8', 0], ["#ebc0fd", 1]]
- })
- }
- }
2. 添加动画效果
这里使用的动画效果是通过animateTo显式动画实现的。animateTo显式动画可以设置组件从状态A到状态B的变化动画效果,包括样式、位置信息和节点的增加删除等,开发者无需关注变化过程,只需指定起点和终点的状态。animateTo还提供播放状态的回调接口,是对属性动画的增强与封装。
添加状态变量opacityValue和scaleValue并初始化为0,分别用于表示透明度和放缩的倍数,动画效果中实现这两个数值从0到1,即可实现Logo的渐出和放大效果。
定义一个贝塞尔曲线cubicBezier,Curves.cubicBezier(0.1, 0.2, 1, 1)。由于需要使用到动画能力接口中的插值计算,故需要导入curves模块。@ohos.curves模块提供了线性Curve. Linear、阶梯step、三阶贝塞尔(cubicBezier)和弹簧(spring)插值曲线的初始化函数,可以根据入参创建一个插值曲线对象。
在animateTo显式动画中,设置动画时长(duration)为2s,延时(delay)0.1s开始播放,设置显示动效event的闭包函数(curve),即起点状态到终点状态为透明度opacityValue和大小scaleValue从0到1。
index.ets:
- @Entry
- @Component
- struct Index {
- @State private opacityValue: number = 0
- @State private scaleValue: number = 0
- private curve1 = Curves.cubicBezier(0.1, 0.2, 1, 1)
- build() {
- Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
- Column(){
- Text('渐变色盘')
- .fontColor('#cf6cc9')
- .fontSize(60)
- .fontStyle(FontStyle.Italic)
- .fontWeight(700)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- Text('一个懂你的调色盘')
- .fontColor('#ee609c')
- .fontSize(40)
- .fontStyle(FontStyle.Italic)
- .fontWeight(600)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- .margin({ top: -5 })
- }
- .scale({ x: this.scaleValue, y: this.scaleValue })
- .opacity(this.opacityValue)
- .onAppear(() => {
- animateTo({
- duration: 2000,
- curve: this.curve1,
- delay: 100,
- }, () => {
- this.opacityValue = 1
- this.scaleValue = 1
- })
- })
- }
- .width('100%')
- .height('100%')
- .linearGradient({
- angle: 120,
- colors: [['#d9ded8', 0], ["#ebc0fd", 1]]
- })
- }
- }
3. 添加动画结束跳转效果
先创建一个main.ets文件。
在animateTo显示动画播放结束的onFinish回调接口中,调用定时器Timer的setTimeout接口延时1.5s后,调用router.replace,显示mainpage.ets页面,其中需要导入router模块。
index.ets:
- // @ts-nocheck
- import router from '@system.router'
- import Curves from '@ohos.curves'
- @Entry
- @Component
- struct Index {
- @State private opacityValue: number = 0
- @State private scaleValue: number = 0
- private curve1 = Curves.cubicBezier(0.1, 0.2, 1, 1)
- build() {
- Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
- Column(){
- Text('渐变色盘')
- .fontColor('#cf6cc9')
- .fontSize(60)
- .fontStyle(FontStyle.Italic)
- .fontWeight(700)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- Text('一个懂你的调色盘')
- .fontColor('#ee609c')
- .fontSize(40)
- .fontStyle(FontStyle.Italic)
- .fontWeight(600)
- .fontFamily('华文行楷')
- .textAlign(TextAlign.Center)
- .margin({ top: -5 })
- }
- .scale({ x: this.scaleValue, y: this.scaleValue })
- .opacity(this.opacityValue)
- .onAppear(() => {
- animateTo({
- duration: 2000,
- curve: this.curve1,
- delay: 100,
- onFinish: () => {
- setTimeout(() => {
- router.replace({ uri: "pages/main" })
- }, 1500);
- }
- }, () => {
- this.opacityValue = 1
- this.scaleValue = 1
- })
- })
- }
- .width('100%')
- .height('100%')
- .linearGradient({
- angle: 120,
- colors: [['#d9ded8', 0], ["#ebc0fd", 1]]
- })
- }
- }
三、主页面
1. 添加背景
主页面的背景和欢迎页面的背景几乎一样,这里就不重复啰嗦了。
mainp.ets:
- @Entry
- @Component
- struct Main {
- private swiperController: SwiperController = new SwiperController()
- build() {
- Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
- }
- .width('100%')
- .height('100%')
- .linearGradient({
- angle: 120,
- colors: [['#d9ded8', 0], ["#ebc0fd", 1]]
- })
- }
- }
2. 添加按钮
Navigator
Navigator:路由容器组件,提供路由跳转能力
参数:
target:必填,指定跳转目标页面的路径,参数类型为string,即直接填页面路径
type:非必填,指定路由方式
- NavigationType.Push:跳转到应用内的指定页面(默认)
- NavigationType.Replace:用应用内的某个页面替换当前页面,并销毁被替换的页面
- NavigationType.Back:返回上一页面或指定的页面
属性:
- active:当前路由组件是否处于激活状态,处于激活状态时,会生效相应的路由操作,参数类型为boolean,即true或false
- params:跳转时要同时传递到目标页面的数据,可在目标页面使用router.getParams()获得
从效果图可以看出按钮的样式是一致的,因此我们可以使用装饰器@Component自定义按钮。通过Navigator容器组件为按钮Button添加路由功能。变量str记录按钮文本,变量str记录页面路径,添加状态变量active初始化为false,在按钮的点击事件中对状态变量active赋值为true,这样就能当点击按钮时,使Navigator处于激活状态时,生效相应的路由操作。
mainp.ets:
- //import router from '@system.router'
- @Entry
- @Component
- struct Main {
- private swiperController: SwiperController = new SwiperController()
- build() {
- Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
- setButton({ str: '线性渐变', uri: 'pages/LinearGradient' })
- setButton({ str: '角度渐变', uri: 'pages/SweepGradient' })
- setButton({ str: '径向渐变', uri: 'pages/RadialGradient' })
- }
- .width('100%')
- .height('100%')
- .linearGradient({
- angle: 120,
- colors: [['#d9ded8', 0], ["#ebc0fd", 1]]
- })
- }
- }
- @Component
- struct setButton{
- @State active: boolean = false
- private str: string
- private uri: string
- build(){
- Navigator({ target: this.uri, type: NavigationType.Push }){
- Button({ type: ButtonType.Normal, stateEffect: true }){
- Text(this.str)
- .fontFamily('方正舒体')
- .fontSize(40)
- .fontWeight(800)
- .fontColor('#FDEB82')
- }
- .width(170)
- .height(80)
- .borderRadius(10)
- .borderColor('#A168FE')
- .borderWidth(2)
- .backgroundColor('#DEB0DF')
- .onClick(() => {
- this.active = true
- })
- }
- .margin(10)
- .active(this.active)
- }
- }
- //通过router添加路由功能
- /*@Component
- struct setButton{
- private str: string
- private uri: string
- build(){
- Button({ type: ButtonType.Normal, stateEffect: true }){
- Text(this.str)
- .fontFamily('方正舒体')
- .fontSize(40)
- .fontWeight(800)
- .fontColor('#FDEB82')
- }
- .width(170)
- .height(80)
- .borderRadius(10)
- .borderColor('#A168FE')
- .borderWidth(2)
- .backgroundColor('#DEB0DF')
- .margin(10).onClick(() => {
- router.push({ uri: this.uri })
- })
- }
- }*/
文章相关附件可以点击下面的原文链接前往下载
https://harmonyos.51cto.com/resource/1570