router.pushUrl({ url: url, //页面路由url params: { //跳转时的携带的参数 args1: this.sex, args2: this.type, } }, router.RouterMode.Single //单实例模式。 //如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近的页面会被移动到栈顶,移动后的页面为新建页。 //如目标页面的url在页面栈中不存在同url页面,按标准模式跳转。 ) }
特别注意:如果想进行页面的跳转,你得让他知道你的基本路由,所以在添加新的界面的时候,一定要在 src/main/resources/base/profile下的main_pages.json进行路由配置,将新建界面的路由加进去才可以进行所谓的页面跳转
//main_pages.json { "src": [ "pages/Index", "pages/EmptyPage", "pages/Topic", "pages/ShowResPage" ] }
答题界面搭建
答题界面采用了组件的方式,将一个个模块按照组件的方式进行封装,然后进行调用,这样的话,在一定程度上可以精简页面,提高代码的复用性。相比index界面,答题界面的代码明显比较简洁,其中标题,进度条,题目答题区域都被封装成组件提取出去,在本页面使用时只需import进来,传入相关的参数即可。
//Topic.ets import { TitleComponent } from '../component/TitleComponent' import { ProgressComponent } from '../component/ProgressComponent' import { TopicBodyComponent } from '../component/TopicBodyComponent' import router from '@ohos.router'
@Entry @Component struct Topic { @State paramsFromIndex: object = router.getParams() @State finishTopic: number = 0 @State allTopic: number = 0
build() { Column({ space: 20 }) { //标题 TitleComponent({ title: this.paramsFromIndex?.['args2'] + "测试 " }) //进度条 ProgressComponent({ finishTopic: this.finishTopic, allTopic: this.allTopic }) //答题界面 TopicBodyComponent({finishTopic:allTopic}) } .width('100%') .height('100%') .backgroundColor("#f0f0f0") } }
那么对于答题界面而言,topic.ets为父组件,标题,进度条,答题区域都为子组件。
进入该界面之后呢 首先得进行首页传值的接收,接收所传递的是哪种测试,同时将标题中界面展示字样进行修改。由于标题字样仅仅是做展示使用,也不会对这个值进行修改,父组件怎么传递,标题怎么渲染即可,所以父子组件之间传递值是属于单向传递,所以对于子组件中变量的监听与接收使用注解**@Prop**即可
/***
- 顶部状态栏 */ import router from '@ohos.router'
@Component export struct TitleComponent { @Prop title: string //父子传递为单向 父--->子
build() { Row({ space: 22 }) { Image($r('app.media.left')).width(50).height(50).fillColor(Color.White) .margin({ left: 15 }) .onClick(() => { router.back() }) Text(this.title).fontSize(20) }.height("10%") .width('100%') .border({ width: 1 })
} }
在题目展示及答题区组件加载之前,先去加载后台数据,获取题目列表数据进行解析,获取到题目列表和题目总数。在答题的过程中,每当用户答完一道题后,已完成数目便会+1,由于父组件与该组件之间是**@Link**进行的双向数据传递,所以在该组件中获取到的数据,以及对于变量的修改,在父组件重视可以被感知的。
//渲染之前进行网络请求 aboutToAppear(): void { //初始化数据 let list = []
//获取网络请求 let httpRequest = http.createHttp(); httpRequest.request("localhost:8899/homp/getAll", (err, data) => { if (!err) { //数据解析 const response = data.result.toString(); const res = JSON.parse(response).data
for (let i = 0; i < res.length; i++) { let item = res[i]; list.push({ id: item.sequenceNumber, name: item.name, optionA: item.optionA, valA: item.valA, optionB: item.optionB, valB: item.valB, }); // console.log(list[i].id) } this.allTopic = res.length //进行总题目数的修改 } else { console.info('error:' + JSON.stringify(err)); } });
this.data = new MyDataSource(list) }
特别注意:由于网络请求这块不算做是默认存在的,得手动开启网络访问权限之后,才可以进行网络数据的获取,主要是对src/main/路径下的module.json5文件进行添加如下代码
//网路权限 "requestPermissions": [ { "name": "ohos.permission.INTERNET", "usedScene": { "when": "always" } } ]
而在主体答题区呢 主要是用到了一个Swiper组件,类似于实现答一题进行自动翻页的效果,并将获取到的数据进行循环渲染。而在游标到达最后一题,并且已经答完时,跳转至结果展示界面等待后端计算返回性格测评的结果
//答题区 Swiper(this.swiperController) { LazyForEach(this.data, (item: Topic) => {
//嵌套组件 显示题目 Column() { //题干 Text(item.id + ". " + item.name)
//选项A Button() { Row() { Text(item.optionA) } } .type(ButtonType.Normal) .optionStyle() .onClick(() => { let index = Number(item.id) this.finishTopic = index if (index === this.data.totalCount()) { //1-保存值 this.res.push(item.valA) let ans = this.res.join('') console.log("ans:" + ans) //跳转界面(携带拼接好的选项字符串) router.replaceUrl({ url: 'pages/ShowResPage', params: { ans: ans, } })
} else { //1-保存值 this.res.push(item.valA) //2-换到下一题 this.swiperController.showNext() }
})
//选项B Button() { Row() { Text(item.optionB) } }.type(ButtonType.Normal) .optionStyle() .onClick(() => { let index = Number(item.id) this.finishTopic = index if (index === this.data.totalCount()) { //如果到最后一题了 显示提交按钮 //1-保存值 this.res.push(item.valB) let ans = this.res.join('') console.log("ans:" + ans) //跳转界面 router.replaceUrl({ url: 'pages/ShowResPage', params: { ans: ans, } })
} else { //1-保存值 this.res.push(item.valB) //2-换到下一题 this.swiperController.showNext() }
})
}.width('90%') .height(180) .justifyContent(FlexAlign.SpaceEvenly) }, item => item)
} .cardStyle() //自定义卡片样式 .cachedCount(2) .index(0) .interval(4000) .indicator(false) .loop(false) .duration(1000) .itemSpace(0) .disableSwipe(true) .curve(Curve.Linear) .onChange((index: number) => { // console.info(index.toString() + this.res.join('')) })
答题进度中,由于父组件与展示题目子组件之间数据类似于双向绑定,在答题组件进行操作,答题后,会对已完成题目这个变量进行修改,而变量的变化能够被子组件感知并单向传递给进度组件,所以整个过程中,进度组件的显示也会随答题而发生变化
/***
- 题目列表中的进度模块 */ @Component export struct ProgressComponent { @Prop finishTopic: number @Prop allTopic: number
build() { Column() { Row() { Text('答题进度:') .fontSize(20) .fontWeight(FontWeight.Bold) Stack() {//堆叠组件,将一个进度条和两个文本框进行堆叠,展示出比较好看的效果 Progress( { value: this.finishTopic, total: this.allTopic, type: ProgressType.Ring
}).width(80) Row() { Text(this.finishTopic.toString()) .fontWeight(18) .fontColor("#36D") Text(' / ' + this.allTopic.toString()) .fontWeight(18) .fontColor(Color.Black) } }
}.cardStyle() .margin({ top: 15, left: 10, right: 10 }) .justifyContent(FlexAlign.SpaceEvenly) .backgroundColor("#FAEBD7")
}
} } //自定义卡片样式 @Styles function cardStyle() { .width("95%") .padding(20) .backgroundColor(Color.White) .borderRadius(15) .shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 }) }
那为了方便大家对于这块的理解,我做了如下的图示,方便大家理解父子组件传值在本案例中的应用
结果展示界面搭建
结果展示界面也是将一些布局以组件的形式提取出去,方便以后的复用等,为了防止一些内容显示不下,整个页面是在一个滚动组件中进行的布局,滚动布局中还是包括三段,顶部提示栏,图片展示栏,以及底部的属性面板;
在进入该页面时,在界面加载渲染之前同样是接收答题界面传递过来的答题选项字符串,在接收到这个答题选项字符串后进行后端数据的请求,请求采用post请求,携带结果字符串,经过后端接口计算后返回测试结果以及性格属性值
//post请求
aboutToAppear() {
//获取网络请求
let httpRequest = http.createHttp();
httpRequest.request("localhost:8899/homp/submit",
{
method: http.RequestMethod.POST,
extraData: {
"ans": this.paramsFromIndex?.['ans'],
}
},
(err, data) => {
if (!err) {
const response = data.result.toString();
const res = JSON.parse(response).data
this.resShow = res; console.log("this.resShow.res:" + this.resShow.res) this.resStr = this.resShow.res console.log("resStr:" + this.resStr)
} }) }
这三部分分别对应着三个自定义组件,标题组件之前有提到过,在这不做过多的赘述,我们重点看一下其他两个组件
图片组件
在结果展示页面获取到后端返回的测试结果后,解析成为一个结果对象,获取到对象的性格简称,传递给图片组件,图片组件在拿到性格简称之后,通过字符串拼接获取到资源文件下的相关性格图片进行展示。同时还为图片添加了简单的动画效果
@Component // @Entry export struct ImageComponent { @State clicked: boolean = false; @Prop resStr: string
// @State resStr: string = 'ENTP'
build() { Row() { Column() { Image($rawfile(this.resStr + ".png")) .width("60%") .height("60%") .objectFit(ImageFit.Contain) // .border({ width: 1 }) .rotate({ x: 0, y: 1, z: 0, angle: this.clicked ? 360 : 0 }) .scale( this.clicked ? { x: 1.25, y: 1.25 } : { x: 1, y: 1 } ) .opacity(this.clicked ? 0.6 : 1) .onClick(() => { this.clicked = !this.clicked; }) .animation( { delay: 10, duration: 1000, iterations: 1, curve: Curve.Smooth, playMode: PlayMode.Normal } ) Text(this.resStr) .fontSize(25) .width("90%") .height("20%") .decoration({ type: TextDecorationType.Underline, color: Color.Orange }) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center)
}.justifyContent(FlexAlign.SpaceEvenly)
} .height("30%") .cardStyle() .margin({ top: 15 }) // .border({ width: 1 }) .alignItems(VerticalAlign.Center) .justifyContent(FlexAlign.Center) } }
属性面板组件
在结果展示页面获取到后端返回的测试结果后,解析成为一个结果对象,将这个对象使用**@ObjectLink**整体作为数据进行传递到属性面板组件,在拿到这个对象后,获取相关的一些字段进行渲染即可
在进行一些占比数据的渲染时,使用到了HarmonyOs内置滑动条组件( Slider),展示一些占比情况
import { Res } from '../data/RES' //属性面板 @Component // @Entry export struct PropertiesPanelComponent { @ObjectLink resReturn: Res
build() { Column() { Column() { Row() { Text('erate').fontSize(12) Slider({ value: Number(this.resReturn.erate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.erate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('irate').fontSize(12) Slider({ value: Number(this.resReturn.irate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.irate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('srate').fontSize(12) Slider({ value: Number(this.resReturn.srate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.srate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('nrate').fontSize(12) Slider({ value: Number(this.resReturn.nrate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.nrate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('trate').fontSize(12) Slider({ value: Number(this.resReturn.trate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.trate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('frate').fontSize(12) Slider({ value: Number(this.resReturn.frate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.frate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('jrate').fontSize(12) Slider({ value: Number(this.resReturn.jrate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.jrate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround)
Row() { Text('prate').fontSize(12) Slider({ value: Number(this.resReturn.prate), min: 0, max: 40, style: SliderStyle.InSet }).panelStyle()
Text(this.resReturn.prate + " %").fontSize(12) }.width('80%') .justifyContent(FlexAlign.SpaceAround) }.cardStyle()
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新