鸿蒙北向简易日历开辟(附源码)

打印 上一主题 下一主题

主题 1005|帖子 1005|积分 3015

目录
前言
一.界说变量
二.编辑日历页面 
1.框架
2.编写页面标题
3.搜索栏及跳转按钮
1)搜索框
2)搜索按钮
3)设置跳转按钮
4.日历主体布局 
5.日历空格函数
6.日历布局函数
三.源代码

前言

本文是记录笔者举行鸿蒙北向学习时,用ArkTs编写一个可以自顺应屏幕的简易日历的开辟过程。(真的非常简易!没有太高技能含量hhhh)源代码附在文章末尾,如有不敷之处接待一起讨论~
制品就是如许啦->

右上角输入年月,按下“go”按钮即可跳转至指定年月;按‘<’或‘>’即可切换至上个月或下个月。
下面是相关代码及开辟过程 ^~^

一.界说变量



  • 使用riqi数组储存页面表现的日期,没有日期的几天使用空格占位。
  • 用week数组存储星期,用于后续设置星期栏
  • 界说动态变量年year月month日day(以2024.12作为初始页面)
  1. @Entry
  2. @Component
  3. struct DataPage {
  4.   @State riqi:Array<string>=["1","2","3","4","5","6","7","8",
  5.     "9","10","11","12","13","14","15",
  6.     "16","17","18","19","20","21","22",
  7.     "23","24","25","26","27","28","29","30","31"," "," "," "," "];
  8.   @State week:Array<string>=["日","一","二","三","四","五","六"]
  9.   @State year:number=2024;
  10.   @State month:number=12;
  11.   @State day:number=0;
  12.   //以2024.12的日历作为初始页面
  13.   //对各个变量进行初始化
复制代码
二.编辑日历页面 

主页面主要是使用Grid组件举行页面布局,使用GridItem组件对页面举行分块。(留意build下只能有一个Grid,GridItem下也只能有一个子组件,但是Grid下可以有多个GridItem,并且Grid的子组件必须是GridItem组件
1.框架

  1.   build() {
  2.     Grid() {
  3.     ...............
  4.     }
  5.     .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
  6.     .rowsTemplate(this.riqi.length>35?'1fr 1fr 2fr 2fr 2fr 2fr 2fr 2fr 2fr 1fr':'1fr 1fr 2fr 2fr 2fr 2fr 2fr 2fr 1fr' )
  7.     //根据月份天数判断需要几行
  8. }
复制代码
(接下来的代码均在Grid()下编写)
columntemplate及rowstemplate为设置页面分块及占比的相关属性,官方文档见下方 
Grid组件
https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/ui/arkts-layout-development-create-grid.md
2.编写页面标题

标题要随着年月的改变相应的改变,因此我们需要在text中引用变量,使其表现的年月能随着变量的改变而改变。
(columnstart,columnend以及之后用到的rowstart,rowend均为设置GridItem容器大小的相关属性。有需要的可以去看这个GridItem组件官方文档)
  1. GridItem(){
  2.         Text(`${this.year}年${this.month}月`)
  3.           .fontSize(25)
  4.           .fontWeight(700)
  5.         //根据用户输入的年月更改页面标题
  6.       }.columnStart(1)
  7.       .columnEnd(3)
复制代码
3.搜索栏及跳转按钮

1)搜索框

接下来,我们需要使用TextInput组件设置输入框,并使用onChange事件将状态变量与文本实时绑定,以便更新日历标题
  1. GridItem(){
  2.         TextInput({placeholder:'年'})
  3.           .onChange((value: string) => {
  4.             this.year=Number(value);
  5.           })
  6.           .fontColor("#ff034980")
  7.       }.columnStart(4)
  8.       .columnEnd(5)
  9.       GridItem(){
  10.         TextInput({placeholder:'月'})
  11.           .onChange((value: string) => {
  12.             this.month=Number(value);
  13.           })
  14.           .fontColor("#ff034980")
  15.       }.columnStart(6)
  16.       .columnEnd(6)
  17.       //记录用户输入的年月
复制代码
2)搜索按钮

使用button的onclick事件调用我们编写的查找日期的函数,foreach会根据riqi数组的变化重新渲染页面
  1. GridItem(){
  2.        Button({type:ButtonType.Circle}) {
  3.           Text("go")
  4.        }.height("30")
  5.        .width("70")
  6.        .backgroundColor("#a3ff8c8c")
  7.        .onClick((event: ClickEvent) => {
  8.          this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))
  9.        })
  10.        //点击事件调用findday()函数找到2000.1.1与用户输入的日期相差几天,将其作为参数传入tian()函数中
  11.        //调用tian()函数对该月份的日期进行排版,并以数组形式传给riqi数组,以便后续foreach循环渲染
  12.       }.columnStart(7)
复制代码
 3)设置跳转按钮

使用跳转按钮直接前去上一月或下一月的日历,记得将1月以及12月这种特殊环境考虑在内,通过onclick事件更新相关变量后调用相关函数更新riqi数组,foreach重新渲染页面
  1. GridItem() {
  2.         Column() {
  3.           Blank()
  4.         }
  5.       }.columnStart(1)
  6.       .columnEnd(5)
  7.       //使用blank进行空白占位
  8. GridItem(){
  9.         Button({type:ButtonType.Capsule}) {
  10.           Text("<")
  11.         }.height("30")
  12.         .width("50")
  13.         .backgroundColor("#bcf5d7ff")
  14.         .onClick((event: ClickEvent) => {
  15.           if(this.month==1){
  16.             this.month=12;
  17.             this.year--;
  18.           }else{
  19.             this.month--;
  20.           }
  21.           this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))
  22.         })
  23.       }.columnStart(6)
  24.       .columnEnd(6)
  25.       
  26. GridItem(){
  27.         Button({type:ButtonType.Capsule}) {
  28.           Text(">")
  29.         }.height("30")
  30.         .width("50")
  31.         .backgroundColor("#bcf5d7ff")
  32.         .onClick((event: ClickEvent) => {
  33.           if(this.month==12){
  34.             this.month=1;
  35.             this.year++;
  36.           }else{
  37.             this.month++;
  38.           }
  39.           this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))
  40.         })
  41.       }.columnStart(6)
  42.       .columnEnd(7)
  43.       //跳转前一个或下一个月
复制代码
4.日历主体布局 

使用foreach循环渲染以减少重复代码
  1. ForEach(this.week,(item:string)=>{
  2.         GridItem() {
  3.           Button({ type: ButtonType.Circle }) {
  4.             Text(`${item}`)   //引用变量
  5.               .fontSize(20)
  6.               .fontColor("#ff808080")
  7.           }.width('90%')
  8.           .height('90%')
  9.           .borderRadius(50)
  10.           .backgroundColor("#fffff2c0")
  11.         }
  12.       })//循环渲染星期导航栏
  13.       ForEach(this.riqi, (item:string)=> {
  14.           GridItem() {
  15.             Button( {type: ButtonType.Circle}) {
  16.               Text(`${item}`)
  17.                 .fontSize(20)
  18.             }.width('90%')
  19.             .height('90%')
  20.             .borderRadius(50)
  21.             .backgroundColor(item === " " ? "#6ac8f4f8" : "#9faff8f8")
  22.              //根据这个button有无日期填充不一样的背景颜色
  23.           }
  24.         })//渲染日期
复制代码
5.日历空格函数

由于日历并不是全被日期占满,会有空出来的地方,就像下面那样

因此我们需要找到日历的开头需要几个空格子,这里我们以2000.1.1为参照,盘算当前年月的1号间隔2000.1.1几天,然后除7取余,余数即为当前月份的日历前空格子的个数,然后返回这个余数。(留意闰年)
  1. function findday(y:number,m:number,d:number) {
  2.   let run: Array<number> = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  3.   let feirun: Array<number> = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  4.   let sum: number = 0;
  5.   let nian: number = 2000;
  6.   let yue: number = 1;
  7.   let ri: number = 1;
  8.   let jujin: number = 0;
  9.   for (let j = nian; j < y; j++) {
  10.     //计算前几年有多少天,需区分是否为闰年
  11.     sum = 0;
  12.     if ((j % 400 == 0) || (j % 4 == 0 && j % 100 != 0)) {
  13.       sum += 366;
  14.     } else {
  15.       sum += 365;
  16.     }
  17.     jujin += sum;
  18.   }
  19.   sum = 0; //计算当年有多少天
  20.   if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)) {
  21.     for (let i = 0; i < m-1; i++) {
  22.       sum += run[i];
  23.     }
  24.   } else {
  25.     for (let i = 0; i < m-1; i++) {
  26.       sum += feirun[i];
  27.     }
  28.   }
  29.   jujin += sum;
  30.   jujin = (jujin+6) % 7;
  31.   //计算出要显示的日历前面需要空几格
  32.   return jujin;
  33. }
复制代码
6.日历布局函数

知道日历前空格数后,我们现在来对日历的日期举行排版,为了雅观我们需要将没有日期的几天填充空格让其可以表现,就像如许

因此我们使用3个循环对数组举行填充,然后返回数组(同样的,留意闰年) 
  1. function tian(y:number,m:number,kong:number) {
  2.   let day:Array<string>=new Array (35);
  3.   for(let i=0;i<kong;i++){
  4.     day[i]=" ";
  5.   }//对没有日期的格子填空格
  6.   let d:number;
  7.   let run: Array<number> = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  8.   let feirun: Array<number> = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  9.   let sum: number = 0;
  10.   if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)) {
  11.     d=run[m-1];
  12.   } else {
  13.     d = feirun[m-1];
  14.   }//填充有日期的格子
  15.   let a=0;
  16.   for(let i=kong;i<d+kong;i++){
  17.     a++;
  18.     day[i]=a.toString();
  19.   }
  20.   if(day.length<=35) {
  21.     for (let i = d + kong; i < 35; i++) {
  22.       day[i] = " ";
  23.     }
  24.   }
  25.   else {
  26.     for (let i = d + kong; i < 42; i++) {
  27.       day[i] = " ";
  28.     }
  29.   }//补全剩余空格
  30.   return day;
  31.   //返回day数组给foreach以便进行循环渲染
  32. }
复制代码
如许,我们的日历就制作完成啦 !
三.源代码

  1. @Entry
  2. @Component
  3. struct DataPage {
  4.   @State riqi:Array<string>=["1","2","3","4","5","6","7","8",
  5.     "9","10","11","12","13","14","15",
  6.     "16","17","18","19","20","21","22",
  7.     "23","24","25","26","27","28","29","30","31"," "," "," "," "];
  8.   @State week:Array<string>=["日","一","二","三","四","五","六"]
  9.   @State year:number=2024;
  10.   @State month:number=12;
  11.   @State day:number=0;
  12.   //以2024.12的日历作为初始页面
  13.   //对各个变量进行初始化  build() {    Grid() {      GridItem(){
  14.         Text(`${this.year}年${this.month}月`)
  15.           .fontSize(25)
  16.           .fontWeight(700)
  17.         //根据用户输入的年月更改页面标题
  18.       }.columnStart(1)
  19.       .columnEnd(3)
  20.       GridItem(){
  21.         TextInput({placeholder:'年'})
  22.           .onChange((value: string) => {
  23.             this.year=Number(value);
  24.           })
  25.           .fontColor("#ff034980")
  26.       }.columnStart(4)
  27.       .columnEnd(5)
  28.       GridItem(){
  29.         TextInput({placeholder:'月'})
  30.           .onChange((value: string) => {
  31.             this.month=Number(value);
  32.           })
  33.           .fontColor("#ff034980")
  34.       }.columnStart(6)
  35.       .columnEnd(6)
  36.       //记录用户输入的年月     GridItem(){
  37.        Button({type:ButtonType.Circle}) {
  38.           Text("go")
  39.        }.height("30")
  40.        .width("70")
  41.        .backgroundColor("#a3ff8c8c")
  42.        .onClick((event: ClickEvent) => {
  43.          this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))
  44.        })
  45.        //点击事件调用findday()函数找到2000.1.1与用户输入的日期相差几天,将其作为参数传入tian()函数中
  46.        //调用tian()函数对该月份的日期进行排版,并以数组形式传给riqi数组,以便后续foreach循环渲染
  47.       }.columnStart(7)      GridItem() {        Column() {          Blank()        }      }.columnStart(1)      .columnEnd(5)      GridItem(){        Button({type:ButtonType.Capsule}) {          Text("<")        }.height("30")        .width("50")        .backgroundColor("#bcf5d7ff")        .onClick((event: ClickEvent) => {          if(this.month==1){            this.month=12;            this.year--;          }else{            this.month--;          }          this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))        })      }.columnStart(6)      .columnEnd(6)      GridItem(){        Button({type:ButtonType.Capsule}) {          Text(">")        }.height("30")        .width("50")        .backgroundColor("#bcf5d7ff")        .onClick((event: ClickEvent) => {          if(this.month==12){            this.month=1;            this.year++;          }else{            this.month++;          }          this.riqi=tian(this.year,this.month, findday(this.year,this.month,this.day))        })      }.columnStart(6)      .columnEnd(7)      //跳转前一个或下一个月      ForEach(this.week,(item:string)=>{        GridItem() {          Button({ type: ButtonType.Circle }) {            Text(`${item}`)              .fontSize(20)              .fontColor("#ff808080")          }.width('90%')          .height('90%')          .borderRadius(50)          .backgroundColor("#fffff2c0")        }      })//循环渲染星期导航栏      ForEach(this.riqi, (item:string)=> {          GridItem() {            Button( {type: ButtonType.Circle}) {              Text(`${item}`)                .fontSize(20)            }.width('90%')            .height('90%')            .borderRadius(50)            .backgroundColor(item === " " ? "#6ac8f4f8" : "#9faff8f8")             //根据这个button有无日期填充不一样的背景颜色          }        })//渲染日期    }    .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')    .rowsTemplate(this.riqi.length>35?'1fr 1fr 2fr 2fr 2fr 2fr 2fr 2fr 2fr 1fr':'1fr 1fr 2fr 2fr 2fr 2fr 2fr 2fr 1fr' )//根据月份天数判定需要几行}}function findday(y:number,m:number,d:number) {
  48.   let run: Array<number> = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  49.   let feirun: Array<number> = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  50.   let sum: number = 0;
  51.   let nian: number = 2000;
  52.   let yue: number = 1;
  53.   let ri: number = 1;
  54.   let jujin: number = 0;
  55.   for (let j = nian; j < y; j++) {
  56.     //计算前几年有多少天,需区分是否为闰年
  57.     sum = 0;
  58.     if ((j % 400 == 0) || (j % 4 == 0 && j % 100 != 0)) {
  59.       sum += 366;
  60.     } else {
  61.       sum += 365;
  62.     }
  63.     jujin += sum;
  64.   }
  65.   sum = 0; //计算当年有多少天
  66.   if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)) {
  67.     for (let i = 0; i < m-1; i++) {
  68.       sum += run[i];
  69.     }
  70.   } else {
  71.     for (let i = 0; i < m-1; i++) {
  72.       sum += feirun[i];
  73.     }
  74.   }
  75.   jujin += sum;
  76.   jujin = (jujin+6) % 7;
  77.   //计算出要显示的日历前面需要空几格
  78.   return jujin;
  79. }function tian(y:number,m:number,kong:number) {
  80.   let day:Array<string>=new Array (35);
  81.   for(let i=0;i<kong;i++){
  82.     day[i]=" ";
  83.   }//对没有日期的格子填空格
  84.   let d:number;
  85.   let run: Array<number> = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  86.   let feirun: Array<number> = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  87.   let sum: number = 0;
  88.   if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0)) {
  89.     d=run[m-1];
  90.   } else {
  91.     d = feirun[m-1];
  92.   }//填充有日期的格子
  93.   let a=0;
  94.   for(let i=kong;i<d+kong;i++){
  95.     a++;
  96.     day[i]=a.toString();
  97.   }
  98.   if(day.length<=35) {
  99.     for (let i = d + kong; i < 35; i++) {
  100.       day[i] = " ";
  101.     }
  102.   }
  103.   else {
  104.     for (let i = d + kong; i < 42; i++) {
  105.       day[i] = " ";
  106.     }
  107.   }//补全剩余空格
  108.   return day;
  109.   //返回day数组给foreach以便进行循环渲染
  110. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表