鸿蒙HarmonyOS小项目开发实战(下),阿里面试官必问

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

//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:$finishTopic,allTopic:$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,
d)

        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,
  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值