ArkTS基础快速入门学习

鸿蒙开发ArkTS基础快速学习

说明

阅读本篇文章,适合一些了解过前端开发以及掌握前端框架的读者。

ArkTS介绍

ArkTS语言在TS语言基础上扩展了声明式UI,组件化,状态管理等功能。

官方推荐两种开发模式开发鸿蒙,一种是基于js扩展的类web范式,另一种是基于TS扩展的声明式UI范式(也就是使用ArkTS开发)。

ArkTs的代码基本结构

Index.ets示例文件

js 复制代码
@Entry
@Component
  struct Index {
    @State message: string = 'Hello World'
 
    build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)					
            .fontWeight(FontWeight.Bold)
        }
        .width('100%')
      }
      .height('100%')
    }
  }
js 复制代码
@Entry // 装饰器
@Component // 装饰器
struct Hello{ // Hello属于自定义组件
    //  @State表示 装饰器
    @State myText:string = "World";
    build(){ // UI描述
        Column(){ //系统组件
            Text('Hello ${this.myText}')
            	.fontSize(50)
            Divder()
            Button('Click me')
            	.onClick(()=>{  // 事件方法
                this.myText = 'ArkUI' 
            })
            .height(50) // 属性方法
            .width(100)
            .margin({ top : 20 })
        }
    }
}

ArkTS自定义组件

首先,创建一个Stage的项目,也就是ArkTS项目。

在devcoe studio 编辑中依次点击:File》Create Project 》next 》Project name:ArkTSApplication(项目名字自己去);Model:Stage》Finish

创建完成项目之后,编辑器会自动打开index.ets文件

js 复制代码
// index.ets 初始文件

import MyComponent1 from './MyComponent1'


@Entry // 入口组件装饰器,一个ets文件中,只能使用依次@Entry
@Component // 组件装饰器 ,表示以下内容是一个组件 它只能装饰用struct关键字声明的结构;注意:一个组件也只能有一个@Component装饰器
struct Index {
    // @State 是一个状态装饰器,只要这里的状态发生变化,整个ui就会进行刷新
  @State message: string = 'Hello World'

  build() { // build方法必须被实现,是用来描述这个组件的UI
    Row() { // 表示一行 此时占满了整个页面
      Column() { // 表示一列
        Text(this.message) //Text组件 表示文本组件 其中this.message来自于上面的 @State message
          .fontSize(50) // 设置Text组件的字体大小
          .fontWeight(FontWeight.Bold) // 设置Text组件的字体粗心
        // 使用内部自定义组件
        MyComponent()
        // 使用外部自定义组件
        MyComponent1()
        MyComponent1({title:"首页"}) // 会将title传递到自定义组件中的@State title
        MyComponent1({title:'发现'})
      }
      .width('100%') // 设置Column的高度100%,也就是设置列的高度为100%
    }
    .height('100%') // 设置Row的高度100%,也就是设置行的高度为100%
  }
}
// 自定义组件部分,内容往下看

再上面的基础上,自己自定义一个组件,还是在index.ets文件中

js 复制代码
// 省略上面部分代码
// 自定义组件
@Component
struct MyComponent{
    build(){
        Row(){
            Button("按钮")
        }
    }
}

单独定义组件

当组件比较复杂的时候,就定义在单独的一个文件中。

在src/main/pages中新建一个 MyComponent1.ets

创建文件:在pages目录上右键》new》page,输入文件名即可。

js 复制代码
@Component
// 使用 export default 将当前组件导出去,让其他文件能够导入该组件
export default struct MyComponent1{
    @State title:string = "按钮1"
    build(){
        Row(){
            Button(this.title)
        }
    }
}

build函数注意事项

1.一个build函数中,入口组件只能有一个Row()组件[根组件],而且还得是个容器组件,比如说Image()就是非容器组件。

2.在非入口组件中,build方法的根组件可以是非容器组件,案例如下

js 复制代码
@Component
export default struct MyComponent1{
  @State title:string = "按钮1"
  build(){
      // 非入口组件中,根组件可以是非容器组件
    Image($r('app.media.icon'))
  }
}

3.无论是入口组件还是非入口组件 都不能使用ForEach作为根组件。

4.bulid函数中不能定义本地变量,或者打印输出;变量和打印输出要在bulid函数的上方定义。

5.bulid函数中,不允许调用普通方法:this.方法();

6.使用@Builder修饰的函数,就成了组件,也就可以在build函数中使用,案例见如下:

js 复制代码
@Builder doRender(){
    // 被@Builder修饰过的函数,返回值是个组件
    Text("使用Builder修饰的函数")
}
build(){
    Row(){
       this.deRender() 
    } 
}

7.builder函数中的组件中内部变量可以使用 this.函数名

js 复制代码
@State title:string = "按钮"
doName(){
    return this.title;
}
build(){
    Row(){
       Button(this.doName()) 
    } 
}

8.build函数中可以使用判断语句if;但是不能用switch语句

java 复制代码
private score:number = 78;
build(){
    Row(){
       if(this.score>=90){
           Text("优秀")
       }else if(this.score >= 80){
           Text("良好")
       }else if(this.score >= 70){
           Text("及格")
       }else{
           Text("不及格")
       }
    } 
}

9.build函数中不能使用表达式

js 复制代码
// 这种方式不可以,只能用if判断语句
this.score >= 60 > Text("及格") : Text("不及格")

组件样式设置

js 复制代码
组件(){}.样式 = 样式值
js 复制代码
build(){
    row(){
        
    }
    // 下面这些都是给row组件设置的样式
    .width('80%')
    .height('500px')
    .backgroundColor('#ff0')
    .margin({top:"50px",left:'50px'})
}

生命周期

页面生命周期

onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
onBackPress:当用户点击返回按钮时触发。

组件生命周期

aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build(0函数之前执行。
aboutToDisappear:在自定义组件析构销毁之前执行

生命周期函数

首先在pages文件夹下新建一个页面,就叫LifePages.ets

js 复制代码
import router from '@ohos.router'

@Entry
@Component
struct LifePage {

  @State childShow : boolean = true

  aboutToAppear(){
    console.info("组件将要显示")
  }

  onPageShow(){
    console.info("组件显示")
  }
  onBackPress(){
    console.log("按返回键")
  }

  onPageHide(){
    console.info("页面隐藏")
  }
  aboutToDisappear(){
    console.info("页面将要被销毁")
  }


  build() {
    Row(){
      Column() {
        // this.showChild为true,创建Child子组件,执行Child aboutToAppear
        if (this.childShow) {
          Child()
        }
        // this.showChild为false,删除Child子组件,执行Child aboutToDisappear
        Button('delete Child').onClick(() => {
          this.childShow = false;
        })

        Button("跳转到其他页面").onClick(() =>{
          router.replaceUrl({
            url:"pages/Index"
          })
        })

      }
      .width("100%")
    }
    .height("100%")


  }
}

@Component
struct Child{
  aboutToAppear(){
    console.info("child about appear")
  }
  aboutToDisappear(){
    console.info(("child about to disappear"))
  }

  build(){
    Text("child component")
      .fontSize(100)
      .backgroundColor('#f00')
  }
}

函数式组件@Bulider装饰器

使用 @Bulider修饰的函数,被称为函数式组件,这种方式修饰的组件增加了组件的复用性。

局部函数式组件

js 复制代码
// 这种函数式组件,只能在当前页面使用,不能外部使用
@Bulider 函数式组件名(){
    Text("函数式组件")
}

首先,在pages/下创建一个新页面,就叫BuliderPage.ets

js 复制代码
@Entry
@Component
  struct BuliderPage {
    @State message: string = 'Hello World'
      
      // 使用 @Bulider修饰的函数,被称为函数式组件
      @Bulider MyFunctionComponent(){
          Text("函数式组件")
      }
      
      // 函数式组件可以有多个
      @Bulider MyFunctionComponent2(text){
          // text就是值传递,当text被赋值后,利用事件来触发修改值,组件的UI时不会刷新的
          // 如果想要达到刷新,就要用到@State修饰的变量
          Column(){
              // Text("函数式组件2")
              // 组件也可以传参
              Text(text)
          }.width("400px")
          .backgroundColor("#f00")
      }
      
 
    build() {
      Row() {
        Column() {
            // 使用自定义的函数式组件
            this.MyFunctionComponent()
            this.MyFunctionComponent2(this.message)
        }
        .width('100%')
      }
      .height('100%')
    }
  }

全局函数式组件

js 复制代码
// 定义全局函数式组件
// 参数:值传递;引用传递

@Bulider function GlobalFunctionComponent($$:{param1:string}){
    // GlobalFunctionComponent(param1) 当param1被赋值后,利用事件来触发修改值,组件UI不会刷新
    // 当使用 $$:{param1:string} 这种方式就可以刷新UI组件
    Column(){
        // Text("全局函数式组件")
        Text($$.param1)
    }
    .width("400px")
    .height("200px")
    .backgroundColor("@00f")
}


@Entry
// .........省略下面部分代码.........
Bulid(){
    Row(){
        Column(){
            // 直接调用全局组件,不需要用this
            // this.message来自于省略部分的代码
            // 传参方式: {参数名称:"值"}
            GlobalFunctionComponent({parm1:this.message})
            
            Button("更新message").onClick(()=>{
                this.message = "你好,世界";
            })
        }
    }
}

完整代码

js 复制代码
// 全局函数式组件
@Builder function GlobalFunctionComponent(param1){
  // 这种传递参数的方式,属于值传递,也就是说
  // 当param1被赋值过,利用事件来触发修改值,是不会自动刷新组件的UI的,触发被赋值的参数被@State修饰
  Column(){
    Text(param1)
  }
  .width("400px")
  .height("200px")
  .backgroundColor("#00f")
}


@Builder function GlobalFunctionComponent2($$:{param1:string}){
  //  $$:{参数名:string}  这种传递参数的方式,属于引用传递,也就是说
  // 当param1被赋值过,利用事件来触发修改值,是会自动刷新组件的UI的
  Column(){
    Text($$.param1)
  }
  .width("400px")
  .height("200px")
  .backgroundColor("#00f")
}






@Entry
@Component
struct BuliderPage{
  @State message: string = 'Hello World'
  public text:string =  "组件的传值"

  // 定义函数式组件
  @Builder MyFunctionComponent(){
    Text("局部函数式组件")
  }

  // 定义多个函数式组件,并且能够传参
  @Builder MyFunctionComponent2(text){
    // 这种传参的方式叫做值传递,也就是说,当text被赋值过后,利用事件来触发修改值,不会刷新组件UI
    // 想要刷新组件的UI,那么在调用该组件,传递参数的时候,传的参数得是被@State修饰过的变量
    Text(text)
  }

  build() {
    Row() {
      Column() {
        // 使用局部函数式组件
        this.MyFunctionComponent()

        // 使用可以传参的函数式组件
        this.MyFunctionComponent2(this.message)

        // 使用全局函数式组件(不需要用this),带参数,会不刷新ui
        GlobalFunctionComponent(this.message)

        // 使用全局函数式组件(不需要用this),带参数刷新的
        // 传参方式: {参数名称:"值"}
        GlobalFunctionComponent2({param1:this.message})


        // 尝试使用事件来修改组件中的值
        Button("修改值").onClick(()=>{
          this.message = "修改组件传递的值"
        })

        // 此时,就可以发现,当点击Button的时候
        // GlobalFunctionComponent中的参数值不会变化
        // GlobalFunctionComponent2中的参数值会发生变化


      }
      .width('100%')
    }
    .height('100%')
  }
}

插槽

@BuilderParans装饰器定义插槽

插槽本身无意义,只是在页面布局中占个空位置而已,后期再往整个空位置里插入组件。

通俗的来讲,就是,在页面布局中,提前给组件留个空位,后期再补上

首先创建一个新页面,BuilderParamPage

BuliderParamPage.ets

js 复制代码
// 自定义组件
@Component
struct MyHeader{
  // 1. 定义插槽
  @BuilderParam leftSlot:() => void
  @BuilderParam rightSlot:() => void

  //  2.将插槽放入容器中
  build(){
    Row(){
      Column(){
        // 放入插槽
        this.leftSlot()
      }
      .width("33.33%")
      Column(){
        Text("标题")
      }
      .width("33.33%")
      Column(){
        // 放入插槽
        this.rightSlot()
      }
      .width("33.33%")
    }
  }
}



// 主入口函数
@Entry
@Component
struct BuilderParamPage{
  @State message:string = "hello world"


  // 4.定义函数式组件
  @Builder leftContent(){
    Text("返回")
  }

  @Builder rightContent(){
    Text("更多")
  }

  @Builder leftContent2(){
    Text("back")
  }

  @Builder rightContent2(){
    Text("more")
  }

  build(){
    Column(){
      // 5. 使用带插槽的自定义组件
      MyHeader({leftSlot:this.leftContent,rightSlot:this.rightContent})
      MyHeader({leftSlot:this.leftContent2,rightSlot:this.rightContent2})

      // 上述两行代码,通俗的来讲:
      // 将第39行的组件插入到13行的插槽,将第43行组件插入到22行的插槽中
      // 将第47行的组件插入到13行的插槽,将第51行组件插入到22行的插槽中

    }
    .height("100%")
  }
}

尾随闭包的方式使用插槽

js 复制代码
// 1. 自定义组件
@Component
struct Book{
    // 2. 定义了插槽
    @BuilderParam title:() => void;
    build(){
        Column(){
            // 3.将插槽放入容器中
            this.title()
        }
        .width("400px")
        .height("600px")
        .backgroundColor("#ccf")
        .margin({top:"20px"})
    }
}


// 主入口函数
@Entry
@Component
struct BuilderParamPage{
  @State message:string = "hello world"
  build(){
    Column(){
        // 使用插槽
        Book(){
            // 相当于 把这个组件插入到第8行的插槽中
            Text("三国演义")
        }
        Book(){
            // 相当于 把这个组件插入到第8行的插槽中
            Text("西游记")
            Column(){
                Text("作者:吴承恩")
            }
        }
    }
    .height("100%")
  }
}

@Style装饰器定义样式

@Styles可以将多个重复样式提炼出来,封装成一个方法,然后组件调用这个方法,就能获得该样式。

@Styles支持 局部样式 和 全局样式

局部组件样式

具体案例

首先,新建一个页面,就叫StylePage.ets

js 复制代码
@Entry
@Component
  struct StylePage {
    @State message: string = 'Hello World'
 	@State color:Color = Color.Black;
      // 定义局部组件样式
      @Styles block(){
          // @Styles block(){} 括号中不支持传参
          .width("400px")
          .height("400px")
          // 此时this.color 就是黑色
          .backgroundColor(this.color)
      }
      
    build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)					
            .fontWeight(FontWeight.Bold)
        }
        .block() // 使用局部组件样式
          
         Column() {
          Text(this.message)
            .fontSize(50)					
            .fontWeight(FontWeight.Bold)
        }
        .block() // 使用局部组件样式
          
          Button("改变颜色").onClick(()=>{
              this.color = Color.blue;
          })
      }
      .height('100%')
    }
  }

全局组件样式

js 复制代码
// 定义全局组件样式
@Styles function commomStyles(){
    .height("200px")
    .width("400px")
    .backgroundColor(Color.Green)
}


@Entry
@Component
  struct StylePage {
    @State message: string = 'Hello World'
      
    build() {
      Row() {
         Column() {
          Text(this.message)
            .fontSize(50)					
            .fontWeight(FontWeight.Bold)
        }
        .commomStyles() // 使用全局组件样式
      }
      .height('100%')
    }
  }

@State装饰器定义响应式数据

组件的状态,当被@State修饰后的变量数据更新了,整个UI组件就会跟着更新。

代码案例

首先新建一个页面,就叫StatePage.ets

js 复制代码
@Entry
@Component
  struct StatePage {
    @State message: string = 'Hello World'
      count:number = 1;
      
 
    build() {
      Column() {          
          Text(this.count.toString())
          
          Button("increase").onClick(()=>{
              this.count++
          })
          // 可以发现,当点击按钮的时候,没有被@State修饰的count变量,页面上的数字永远是1,不会更新
          // 当给count添加@State(@State count:Number =1) ,页面上的数字就会更新了
      }
      .height('100%')
    }
  }

@Prop装饰器

使用@Prop装饰器可以实现组件外部向内部传输数据

@Prop装饰器只能实现组件外部向内部传递数据,是单向的。

而且,组件内部对数据的修改,不会影响到组件外部

代码案例

首先新建一个页面,就叫PropPage.ets

js 复制代码
// 定义一个子组件
@Component
struct GoodsItem{
  // 接受外部组件传进来的title和price参数
  @Prop title:string;
  @Prop price:number;

  build(){
    Column(){
      Text(`商品名称:${this.title}`)
      Text(`商品价格:${this.price}`)
        // 这里的this.price--;只会影响该组件内部的price,不会影响到父组件
        .onClick(()=>{
          this.price--;
        })
        .fontSize(24)
    }
  }
}

// 定义接口
interface Good{
  title:string; // 接口属性
  price:number;
}


@Entry
@Component
struct PropPage {
  @State message: string = 'Hello World'
  @State price :number = 0
  @State goodList:Good[] = [
    {
      title:'手机',price:666
    },
    {
      title:'平板',price:777
    },
    {
      title:'电脑',price:888
    },
  ]

  build() {
    Column() {
      Text(`父组件的price:${this.price}`).fontSize(50)

      // 将字符串手机 和 price变量 传递给GoodsItem组件
      GoodsItem({title:"手机",price:this.price})
      
      // 使用ForEach循环遍历数组
      // ForEach(数组,变量=>{回调函数})
      ForEach(this.goodList,item =>{
        GoodsItem({title:item.title,price:item.price})
      })
      
      Button("父组件修改price").onClick(()=>{
        this.price++;
      })

    }
  }
}

@Link装饰器

@Link装饰器可以实现父子双向同步,也就是说可以实现父组件和子组件的数据同步

代码案例

tsx 复制代码
// 定义子组件
@Component
struct Child{
  // 1. 使用@link修饰变量
  @Link author:string;

  build(){
    Text(`作者:${this.author}`)
      .onClick(()=>{
        // 当点击作者文本的时候,作者发生改变
        this.author = "李四";
      })
  }
}



@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  // 2.定义与@Link修饰的相同变量名
  @State author:string = "张三"

  build() {
    Column() {
      Text(`父组件中的author:${this.author}`)

      // 3.往子组件里传值 要使用 $变量
      Child({author:$author})
      Button("父组件")
        .onClick(()=>{
          this.author = "王五"
        })
    }
    .height('100%')
  }
}

// 总结:
// 此时,父子组件就实现了双向数据绑定,当子组件中的author发生变化的时候,父组件的author也跟着发生变化
// 当父组件中的author发生变化的时候,子组件的author也跟着发生变化

LocalStorage本地存储

StoragePage可以提供少量数据的本地存储。

@LocalStorageProp:UI中变量和LocalStorage单向同步。被@LocalStorageProp装饰的变量发生改变,不会影响LocalStorage中变量的值。但是LocalStorage中变量的值发生变化会同步被@LocalStorageProp装饰的变量。

@LocalStorageLink:UI中变量和LocalStorage双向同步。

代码案例

首先,新建一个页面,就叫StoragePage.ets

tsx 复制代码
// 1. 创建LocalStorage实例
let storage = new LocalStorage({"count":100})

// 创建子组件
@Component
struct Child{
  // 这里的count与new LocalStorage中的count保持一致
  @LocalStorageLink("count") childCount : number =1;
  @LocalStorageProp("count") childProp : number =1;
  build(){
    Column(){
      Button(`子组件来自localStorage的数据+1:${this.childCount}`)
        .onClick(()=>{
          this.childCount++;
        })
      Button(`子组件更新LocalStorageProp的数据+2:${this.childProp}`)
        .onClick(()=>{
          this.childProp+=2;
        })
    }
  }
}

@Entry
@Component
struct StoragePage{
  @State message:string = "Hello World"
  @LocalStorageLink("count") parentCount : number =1;
  @LocalStorageProp("count") parentProp : number =1;

  build(){
    Row(){
      Column(){
        // 子组件
        Child()

        Text(`父组件来自localSotrage的数据${this.parentCount}`)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button("父组件更新storage-1").onClick(()=>{
          this.parentCount--
        })
        Button("父组件更新storage prop-2").onClick(()=>{
          this.parentProp-=2;
        })
      }
      .width("100%")
    }
    .height("100%")
  }
}

条件渲染

新建一个页面,就叫ifPage

tsx 复制代码
@Entry
@Component
struct IfPage{
    @State score:number = 10;
    build(){
        Column(){
            Text(`分数:${this.score}`).fontSize(24)
            Button("更新分数").onClick(()=>{
                this.score += 10;
            })
            
            if(this.score < 60){
                Text("不及格").fontSize(24)
            }else if(this.score >=60 && this.score < 70){
                Text("及格").fontSize(24)
            }else if(this.score >=70 && this.score <90){
                Text("良好").fontSize(24)
            }else if(this.score>=90){
                Text("优秀").fontSize(24)
            }
        }
    }
}

列表渲染

新建一个页面,就叫ListPage

tsx 复制代码
@Entry
@Component
struct ListPage{
  @State list:number[] = [11,22,33,66,88]
  build(){
    Column({space:10}){
      // item表示元素本身
      ForEach(this.list,(item:string)=>{
        Row(){
          Text(`内容:${item}`).fontSize(24).fontColor(Color.White)
        }
        .height(100)
        .backgroundColor(Color.Blue)
        .padding(20)
      },(item:string)=>item)
    }.width('100%').height('100%')
  }
}

列表应用案例

新建一个页面,就叫BookListPage

tsx 复制代码
interface Book{
  id:number,
  title:string,
  price:number,
  num:number;
}

// 定义图书组件
@Component
struct BookItem{
  @Prop title:string;
  @Prop price:number;
  @Prop num:number;
  build(){
    Row(){
      Column(){
        Image($r('app.media.icon')).width(100).height(100)
      }.width('40%')
      Column(){
        Row(){
          Text(`图书名称:${this.title}`).fontSize(18)
        }
        Row(){
          Text(`图书价格:${this.price}`).fontSize(14).fontColor(Color.Red)
        }
        Row(){
            Button("-").onClick(()=>{
                this.num--;
            })
            Text(`${this.num}`).fontSize(18).margin(10)
            Button("+").onClick(()=>{
                this.num++;
            })
        }
      }.width("60%")
    }
    .backgroundColor("#eee")
    .width("90%")
    .margin({left:'5%'})
  }
}



@Entry
@Component
struct BookListPage {
  @State bookList: Book[] = [
    {
      id: 0,
      title: "三国演义",
      price: 89.9,
      num:1
    },
    {
      id: 1,
      title: "水浒传",
      price: 79.9,
      num:2
    },
    {
      id: 2,
      title: "西游记",
      price: 99.9,
      num:5
    },
    {
      id: 3,
      title: "红楼梦",
      price: 109.9,
      num:7
    }
  ]

  build() {
    Column({ space: 10 }) {
      ForEach(this.bookList, (item: Book) => {
        BookItem({title:item.title,price:item.price,num:item.num})
      }, (item: Book) => item.id + '')
    }
  }
}

线性布局

行布局

新建一个文件,文件名就叫RowPage.ets

行布局也称水平布局

typescript 复制代码
@Entry
@Component
struct RowPage{
  build(){
    // 行容器元素,子元素水平排列
    Row(){
            Text("水平排列").width("20%").backgroundColor(Color.Yellow)
            Row(){

            }.width("20%").height(100).backgroundColor(Color.	Red)
    Row(){

    }.width("20%").height(100).backgroundColor(Color.	Green)
    Row(){

    }.width("20%").height(100).backgroundColor(Color.	Blue)
  }.height(200)
  .width("100%")
  .backgroundColor("#ccc")
  .justifyContent(FlexAlign.Center)
  .alignItems(VerticalAlign.Center)
}
}

列布局

列布局也称垂直布局

新建一个文件,文件名就叫ColumnPage.ets

typescript 复制代码
@Entry
@Component
struct ColumnPage {

  build() {
    // 列容器元素,子元素在垂直方向
    Column({space:50}){
      Text("垂直排列")
      Row(){
        Text("111")
        Text("222")
        Text("333")
      }.width(200).height(200).backgroundColor(Color.Red)
      Column(){
        Text("111")
        Text("222")
        Text("333")
      }.width(200).height(200).backgroundColor(Color.Green)
    }.width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Start)
  }
}

StackLayout层叠布局

StackLayout层叠布局可以实现元素重叠,类似于css中的绝对定位

新建一个文件,文件名就叫StackLayoutPage.ets

typescript 复制代码
@Entry
@Component
struct StackLayoutPage {

  build() {
    Stack({alignContent:Alignment.Start}){
      Row(){

      }.width(300).height(300).backgroundColor(Color.Pink)
      Text("stack layout").width(200).height(200).backgroundColor(Color.Yellow).zIndex(2)
      Button("按钮").zIndex(3)
    }.width("100%").height(500).backgroundColor(Color.Gray)
  }
}

Flex弹性布局

新建一个文件,文件名就叫FlexPage.ets

typescript 复制代码
@Entry
@Component
struct FlexPage {

  build() {
      // direction:FlexDirection.Row 横向排列
      // wrap:FlexWrap.Wrap 换行
    Flex({direction:FlexDirection.Row,wrap:FlexWrap.Wrap}){
      Text("box1").width(100).height(100).backgroundColor(Color.Red)
      Text("box2").width(100).height(100).backgroundColor(Color.Red)
      Text("box3").width(100).height(100).backgroundColor(Color.Red)
      Text("box4").width(100).height(100).backgroundColor(Color.Red)
      Text("box5").width(100).height(100).backgroundColor(Color.Red)
    }
  }
}

Flex的主轴和交叉轴的排列

typescript 复制代码
@Entry
@Component
struct FlexPage {

  build() {
      // justifyContent:FlexAlign.Center左右居中
      // alignItems:ItemAlign.End 靠下排列
    Flex({justifyContent:FlexAlign.Center,alignItems:ItemAlign.End}){
      Text("box1").width(100).height(100).backgroundColor(Color.Red)
      Text("box2").width(100).height(100).backgroundColor(Color.Red)
      Text("box3").width(100).height(100).backgroundColor(Color.Red)
    }.height(500).backgroundColor(Color.Gray)
  }
}

Flex布局综合练习

首先,新建一个LayoutPage.ets文件

typescript 复制代码
@Entry
@Component
struct LayoutPage {
  @State apps: string[] = ['app1','app2','app3','app4','app5','app6','app7','app8']
  @State app2:string[] = ["微信","电话","短信"]

  build() {
    // 使用Stack层叠布局
    Stack({ alignContent: Alignment.Bottom }) {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.apps, (item: string) => {
          Text(item)
            .width(100)
            .height(100)
            .backgroundColor("#ccc")
            .borderRadius(10)
            .margin(10)
            .textAlign(TextAlign.Center)
        }, item => item)
      }.width("100%").height("100%")

      Flex({ justifyContent: FlexAlign.SpaceBetween }) {
        ForEach(this.app2, (item: string) => {
          Text(item)
            .width(100)
            .height(100)
            .backgroundColor(Color.Green)
            .borderRadius(10)
            .textAlign(TextAlign.Center)
        }, item => item)
      }
      .width("90%")
      .height(100)
      .backgroundColor("#ccc")
      .borderRadius(10)
      .margin({ bottom: 10 })
    }
  }
}

综合项目练习

项目准备

新建一个项目,项目名字就叫jaofei

在pages目录下创建一个文件夹,名叫components

将项目所需要的图片复制到src/main/resources/rawfile目录下

具体代码

1.在components目录下创建一个文件,就叫MyHeader.ets

typescript 复制代码
// MyHeader.ets 文件
@Component
export default struct MyHeader{
    build(){
        Row(){
            Column(){
                // Image组件读取src/main/resources/base/media下的文件:Image($r('app.media.图片名称(不需要带后缀)'))
                Image($r('app.media.shuifei')).width(16).height(16)
            }.width(57)
            // 第二列
            Column(){
                Text("缴费记录")
            }
        }.height(44)
        .width("100%")
        .backgroundColor("#F7F8FA")
    }
}

Index.ets文件

typescript 复制代码
import  MyHeader from './components/MyHeader'
import  JFItem from './components/JFItem'

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  @State list: Object[] = [
    {
      icon:'shuifei.png',
      title:"水费",
      date:"2022-01-09 14:30:20",
      price:"100.00"

    },
    {
      icon:'dianfei.png',
      title:"电费",
      date:"2022-01-09 14:30:20",
      price:"100.00"

    },
    {
      icon:'fangzu.png',
      title:"房租",
      date:"2022-01-09 14:30:20",
      price:"100.00"

    },
    {
      icon:'wuye.png',
      title:"物业费",
      date:"2022-01-09 14:30:20",
      price:"100.00"

    }
  ]

  build() {
    Column(){
      MyHeader()

      Flex(){
        Column(){
          Text("缴费类型").height(56)
        }.width('50%')

        Column(){
          Text("全部时间").height(56)
        }.width('50%')
      }
      .height(56)
      .width('100%')
      .linearGradient({
        direction:GradientDirection.Top,
        colors:[['#F7F8FA',0],['#fff',1]]
      })


      ForEach(this.list,(item) =>{
        JFItem({
          icon:item.icon,
          title:item.title,
          date:item.date,
          price:item.price
        })
      }, item=> item.icon)


    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
}

在component文件夹下创建一个新组件,就叫JFItem.ets

typescript 复制代码
// JFItem.ets
@Component
export default struct JFItem{
    @Prop icon:string = $r('app.media.dianfei');
    @Prop title:string;
    @Prop date:string;
    @Prop price:string;
    build(){
        Row(){
            // dianfei是media文件夹下的dainfei.png图片 访问src/main/resources/rawfile下的资源,就用$rawfile('图片路径')
            Image($rawfile(this.icon)).width(56).height(56).margin({left:16,right:10})

            Column(){
                Text(this.title).textAlign(TextAlign.Start)
                Text(this.date)
            }
            .alignItems(HorizontalAlign.Start)
            Text(`${this.price}元`)
            .width(60)
            .fontSize(14)
            .margin({left:10})
        }
        .width(345)
        .height(90)
        .borderRadius(5)
        .shadow({offsetX:0,offsetY:4,radius:20,color:"#ccc"})
        .margin({top:10})
    }
}

ArkUI内置组件

按钮组件

Button组件

typescript 复制代码
@Entry
@Component
  struct ButtonPage {
    @State message: string = 'Hello World'
 
    build() {
      Row() {
        Column() {
            // 按钮组件
            // Normal方形按钮
            // Capsule 圆角按钮
            // Circle 圆形按钮
            // stateEffect:false 取消按钮点击效果
            Button("按钮",{type:ButtonType.Normal,stateEffect:true})
            // 给方形按钮添加样式
            .borderRadius(10)
            .backgroundColor(0xfff000)
            .fontColor(0x0000ff)
            .fontSize(100)
            Button("按钮",{type:ButtonType.Capsule})
            Button("按钮",{type:ButtonType.Circle})
            
            Button({type:ButtonType.Normal,stateEffect:true}){
               Row(){
                    Image($r('app.media.loading')).width(30).height(30).margin({right:10})
                	Text("正在加载").fontSize(24).fontColor(0xffffff)
               }.padding(10)
            }
            .width(200)
            .borderRadius(5)
            
            Button(){
                Image($r('app.media.delete')).width(50).height(50)
            }.padding(20)
            .onClick(()=>{
                console.info("按钮被点击")
            })
        }
      }
    }
  }

单选按钮

Radio组件

typescript 复制代码
@Entry
@Component
struct RadioPage {
  @State message: string = 'Hello World'
  @State gender:string = "男"

  build() {
    Column(){
        Row(){
            Text(`性别:${this.gender}`).fontSize(24)
        }
      Row() {
        // 单选按钮
        Text("男: ")
        // checked(true) 默认选中
        // .onChange() 给单选按钮绑定事件
        Radio({value:'男',group:'gender'}).checked(true)
          .width(50).height(50)
          .onChange((isCheck:Boolean)=>{
            if(isCheck){
              // 按钮被选中
              this.gender = "男"
            }
          })
        Text("女: ")
        Radio({value:'女',group:'gender'})
          .onChange((isCheck:Boolean)=>{
            if(isCheck){
              // 按钮被选中
              this.gender = "女"
            }
          })
      }
    }
  }
}

多选按钮

Toggle组件

typescript 复制代码
@Entry
@Component
struct TogglePage{
  @State message: string = 'Hello World'
  @State hobbies: string[] = ["唱歌"]
  @State isOpen:boolean = true;

  build() {
    Column() {
      Row() {
        Text(`爱好:${this.hobbies.toString()}`)
      }

      Row() {
        // 多选按钮
        // isOn:true 默认选中
        Text("爱好").fontSize(24)
        Text("唱歌:")
        Toggle({ type: ToggleType.Checkbox, isOn: true })
          .onChange((isOn: boolean) => {
            if (isOn) {
              this.hobbies.push('唱歌')
            } else {
              this.hobbies = this.hobbies.filter(item => item != "唱歌")
            }
          })
        Text("跳舞:")
        Toggle({ type: ToggleType.Checkbox, isOn: false })
          .onChange((isOn: boolean) => {
            if (isOn) {
              this.hobbies.push('跳舞')
            } else {
              this.hobbies = this.hobbies.filter(item => item != "跳舞")
            }
          })
        Text("打篮球:")
        Toggle({ type: ToggleType.Checkbox, isOn: false })
          .onChange((isOn: boolean) => {
            if (isOn) {
              this.hobbies.push('打篮球')
            } else {
              this.hobbies = this.hobbies.filter(item => item != "打篮球")
            }
          })
      }

      Row(){
        Text(`是否打开:${this.isOpen}`)
        // 开关按钮
        Toggle({type:ToggleType.Switch,isOn:true})
          .switchPointColor(Color.Red)//未选中
          .selectedColor(Color.Pink) // 选中
          .onChange((isOn:boolean)=>{
            this.isOpen = isOn
          })
      }

      Row(){
        Toggle({type:ToggleType.Button}){
          Text("是否打开")
        }.selectedColor(Color.Pink)
      }


    }
  }
}

输入框组件

typescript 复制代码
@Entry
@Component
struct InputPage {
  @State message: string = 'Hello World'

  build() {
    Column() {

      // 输入框组件
      TextInput({placeholder:"请输入"})
        .width(300)
        .height(50)
        .backgroundColor(Color.Green)
        .fontSize(24)
        .fontColor(Color.White)


      // 密码输入框
      TextInput().type(InputType.Password)
      // 数字输入框
      TextInput().type(InputType.Number)

      // 获取输入框的内容
      TextInput().onChange((value:string)=>{
        console.info(value)
      })

      // 多行输入框
      TextArea().width(200).height(100)
        .backgroundColor(0xcccccc)
          // 获取多行输入框的值
        .onChange((value:string)=>{
          console.info(value)
        })

    }
  }
}

注册表单案例

typescript 复制代码
@Entry
@Component
struct RegistPage {
  @State message: string = 'Hello World'

  build() {
    Column() {
      Row(){
        Text("姓名:")
        TextInput().width(300)
          .height(50)
      }
      Row(){
        Text("密码:")
        TextInput()
          .width(300)
          .type(InputType.Password)
          .height(50)
      }.margin({top:10})
      Row(){
        Button("提交")
          .width(300)
          .onClick(()=>{

          })
      }
    }
  }
}

页面跳转

页面跳转有两种方式,一种是用pushUrl跳转,另一种是用replaceUrl进行跳转。

使用Index.ets页面,新建一个ListPage.ets页面

Index.ets

typescript 复制代码
// 导入路由对象
import router from '@ohos.router'
@Entry
@Component
  struct Index {
    @State message: string = 'Hello World'
 
    build() {
      Column() {
          Button("跳转到list页面").onClick(()=>{
              // pushUrl会在当前页面添加一个页面
              // 下一页可以实现上一页返回操作
              router.pushUrl({
                  url:"pages/ListPage"
              })
          })
          
          
          Button().onClick(()=>{
              // replaceUrl直接替换当前页面
            // 下一页无法实现返回操作
              router.replaceUrl({
                  url:"Pages/ListPage"
              })
          })
           	
      }
    }
  }

ListPage.ets

typescript 复制代码
import router from '@ohos.router'
@Entry
@Component
  struct ListPage {
    @State message: string = 'Hello World'
 
    build() {
      Column() {
          Text("list 页面").fontSize(32)
          Button("返回").onClick(()=>{
              // 返回到用pushUrl跳转过来的页面
              router.back()
          })
      }
    }
  }

路由跳转练习

新建DetailPage.ets页面

typescript 复制代码
import router from '@ohos.router'
@Entry
@Component
struct DetailPage {
  @State title: string = 'Hello World'
  @State price: number = 0
    
  // 当页面一开始加载就会自动执行onPageShow函数
  onPageShow(){
      // 获取路由传来的参数
      const params = router.getParams()
      this.title = params['title']
      this.price = params['price']
      
  }

  build() {
    Column() {
        Button("返回").onClick(()=>{
            router.back()
        })
        Text("详情页").fontSize(24)
        
        Text(`名称${this.title}`)
        Text(`名称${this.price}`)
    }
  }
}

ListPage.ets

typescript 复制代码
import router from '@ohos.router'
@Entry
@Component
  struct ListPage {
    @State message: string = 'Hello World'
      @State list:Object[] = [
          {
              id:"1",
              title:"三国演义",
              price:78.8
          },
          {
              id:"2",
              title:"西游记",
              price:78.8
          },
          {
              id:"3",
              title:"红楼梦",
              price:78.8
          },
          {
              id:"4",
              title:"水浒传",
              price:78.8
          }
		]
 
    build() {
      Column() {
          Text("list 页面").fontSize(32)
          Button("返回").onClick(()=>{
              // 返回到用pushUrl跳转过来的页面
              router.back()
          })
          
          Column(){
              ForEach(this.list,(item)=>{
                  Row(){
                      Text(item.title)
                  }.height(50).width("100%").backgroundColor(0xeeeeee).margin({top:10})
                  .onClick(()=>{
                      router.pushUrl({
                          url:"pages/DetailPage",
                          params:item
                      })
                  })
              },(item)=>item.id)
          }.width("100%")
      }.width("100%").height("100%")
    }
  }

模板文件待删除

typescript 复制代码
@Entry
@Component
struct TextPage {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

综合练习

做一个todolist,待办任务。

新建一个工程项目,就叫TodoList。

在pages目录下,新建一个TodoList.ets文件

typescript 复制代码
// TodoList.ets
// 导入Todos文件
import Todos from './components/Todos'
import {ObjectData,ListData} from '../viewmodel/DataModel'

@Entry
@Component
struct TodoList {
  // 所有的列表
  @State myLists: Array<ObjectData> = []
  // 当前要渲染的列表
  @State curList:Array<ObjectData> = []
  @State inputText:string = ""

  aboutToAppear(){
    this.myLists = new ListData().getList()
    this.curList = this.myLists;
  }

  build() {
    Stack({alignContent:Alignment.Bottom}){
      Row() {
        Column() {
          Text("任务列表")
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
            .margin({top:20})

          Row({space:20}){
            Button("全部").backgroundColor(Color.Red)
              .onClick(()=>{
                this.curList = this.myLists
              })
            Button("已完成").backgroundColor(Color.Blue)
              .onClick(()=>{
                // 使用过滤器filter来过滤出status为true的对象
                this.curList = this.myLists.filter(item=>item.status)
              })
            Button("未完成").backgroundColor(Color.Blue)
              .onClick(()=>{
                // 使用过滤器filter来过滤出status为false的对象
                this.curList = this.myLists.filter(item=>!item.status)
              })
          }.margin({top:20})

          // 调用外部组件
          // 调用Array<ObjectData>类型数据,要用$
          Todos({lists:$curList})
        }
        .width("100%")
        .height("100%")
        .alignItems(HorizontalAlign.Center)
      }
      .height('100%')
      .backgroundColor(0xcccccc)
      .alignItems(VerticalAlign.Top)


      // 输入框
      Row(){
        // text:this.inputText 表示将this.inputText的值显示到输入框中
        TextInput({placeholder:"请输入待办任务",text:this.inputText})
          .width("60%")
          .height(50)
          .backgroundColor(Color.White)
          .onChange(value=>{
            this.inputText = value
          })
        Button("添加")
          .width("30%")
          .height(50)
          .margin({left:20})
          .onClick(()=>{
            if(this.inputText){
              this.myLists.push(new ObjectData(this.myLists.length+1,this.inputText,false))
              // 清空输入框
              this.inputText = ""
            }

          })
      }
      .margin({bottom:20})
    }
  }
}

在pages目录下新建一个文件夹,就叫components,然后再在该目录下新建一个Todos.ets文件

tsx 复制代码
// Todos.ets
import {ObjectData,ListData} from '../../viewmodel/DataModel'
@Component
export default struct Todos{
  @Link lists:Array<ObjectData>;

  scroller:Scroller = new Scroller()

  @Builder
  taskItem(item:ObjectData){
    Row(){
      // 使用复选框组件
      Toggle({type:ToggleType.Checkbox,isOn:item.status})
        .onChange(event => {
          // 找出当前被点击的对象
          const curTodo = this.lists.find(todo => todo.id === item.id)
          curTodo.status = event;
        })
      Text(item.task)
        .margin({left:20})
    }
    .width("90%")
    .height(50)
    .padding({left:20})
    .margin({top:20})
    .backgroundColor(Color.White)
    .borderRadius(20)
  }

  build(){
    // Scroll使用滚动组件,实现页面滚动
    Scroll(this.scroller){
      Column(){
        ForEach(this.lists,(item)=>{
          this.taskItem(item)
        },item=>item.id)
      }
    }
    .scrollable(ScrollDirection.Vertical) // 设置滚动方向为垂直滚动
    .scrollBar(BarState.On) // 设置开启滚动条
    .scrollBarColor(Color.Gray) // 设置滚动条颜色
    .scrollBarWidth(10) //设置滚动条宽度
    .edgeEffect(EdgeEffect.Spring)
    .margin({bottom:200})
  }
}

在ets文件夹下新建一个目录,就叫viewmodel,然后在该文件夹下新建一个文件,就叫DataModel.ets

typescript 复制代码
// DataModel.ets
export class ObjectData{
    id:number;
    task:string;
    status:boolean;
    
    constructor(id:number,task:string,status:boolean){
        this.id = id;
        this.task = task;
        this.status = status
    }
}

export class ListData{
    private lists:Array<ObjectData> = [
        new ObjectData(1,"吃饭",true)
        new ObjectData(2,"唱歌",false)
        new ObjectData(3,"写作业",false)
    ]
    
    getList(){
        retrun this.lists
    }
}
相关推荐
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__4 小时前
Web APIs学习 (操作DOM BOM)
学习
数据的世界016 小时前
.NET开发人员学习书籍推荐
学习·.net
四口鲸鱼爱吃盐6 小时前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
OopspoO8 小时前
qcow2镜像大小压缩
学习·性能优化
A懿轩A9 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
居居飒9 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
kkflash39 小时前
提升专业素养的实用指南
学习·职场和发展
1 9 J10 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
6.9411 小时前
Scala——身份证号码查询籍贯
学习·scala