# arkts鸿蒙学习

# arkts组件

# Image

图片需要再module.json5下面配置权限

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

# Text

# TextInput

# Button

# Slider

# arkts 布局

  Row(){
        Image($r('app.media.icon'))
        .width(this.imgWidth)
        .interpolation(ImageInterpolation.High)

    }.justifyContent(FlexAlign.Start)
    .height(220)
    // .width("100%")

给了width后图片居左,但是没给width图片居中,为什么?

# arkts 其他组件

  • Blank 空白
  • Stack 堆叠
  Stack(){
      Progress({
        value: this.stat.finishTask,
        total: this.stat.totalTask,
        type: ProgressType.Ring
      })
        .width(100)
      Row(){
        Text(this.stat.finishTask.toString())
          .fontSize(24)
          .fontColor('#36D')
        Text(' / ' + this.stat.totalTask.toString())
          .fontSize(24)
      }
    }

# arkts ForEach和ifelse

class Item {
  name: string
  image: ResourceStr
  price: number
  discount: number

  constructor(name: string, image: string, price: number, discount: number = 0) {
    this.name = name
    this.image = image
    this.price = price
    this.discount = discount
  }
}

@Entry
@Component
struct Items {

  // 商品数据
  private items: Array<Item> = [
    {name:'z',image:"f",price:1,discount:2},
    new Item('华为Mate60', 'meta60',6999, 500),
    new Item('MateBookProX', "mateBookProX",13999),
    new Item('WatchGT4', 'watchGT4',1438),
  ]

  build() {
    Column() {
      ForEach(this.items,(item:Item)=>{
        Row(){
          Button(item.name)
          Text(item.price+'')
          if(item.discount){
            Button('y')
          }else{
            Button("x")
          }
        }.height(100).width("100%")
      })
    }
    .width('100%')
    .height('100%')
  }
}

# arkts List ListItem

List区域可滑动,需要用ListItem包裹,layoutWeight类似flex

    Column() {
      Row(){
        Text('標題')
      }.height(50).width("100%")
      // 商品列表部分
      List({space: 8}){
        ForEach(
          this.items,
          (item: Item) => {
            ListItem(){
              Row(){
                Button(item.name)
                Text(item.price+'')
                if(item.discount){
                  Button('y')
                }else{
                  Button("x")
                }
              }.height(100).width("100%")
            }
          }
        )
      }
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
// 滑动
List({space: 10}){
        ForEach(
          this.tasks,
          (item: Task, index) => {
            ListItem(){
              TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})
            }
            .swipeAction({end: this.DeleteButton(index)})
          }
        )
      }

# arkts 代码复用

  1. @Component
  2. @Builder
  3. @Styles
  4. @Extends
  @Builder DeleteButton(index: number){
    Button(){
      Image($r('app.media.ic_public_delete_filled'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.tasks.splice(index, 1)
      this.handleTaskChange()
    })
  }
}

# arkts 状态管理

@Entry
@Component
struct PropPage {
  // 统计信息
  @Provide stat: StatInfo = new StatInfo()

  build() {
    Column({space: 10}){
      Header()

      // 1.任务进度卡片
      TaskStatistics()

      // 2.任务列表
      TaskList()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}
@Component
struct TaskStatistics {
  @Consume stat: StatInfo

  build() {
    Row(){
      Text('任务进度:')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Stack(){
        Progress({
          value: this.stat.finishTask,
          total: this.stat.totalTask,
          type: ProgressType.Ring
        })
          .width(100)
        Row(){
          Text(this.stat.finishTask.toString())
            .fontSize(24)
            .fontColor('#22FF33')
          Text(' / ' + this.stat.totalTask.toString())
            .fontSize(24)
        }
      }
    }
    .card()
    .margin({top: 5, bottom: 10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}
@Observed
class Task{
  static id: number = 1
  // 任务名称
  name: string = `任务${Task.id++}`
  // 任务状态:是否完成
  finished: boolean = false
}
// TaskItem
@Component
struct TaskItem {
  // 获取深度的状态
  @ObjectLink item: Task
  // 获取方法
  onTaskChange: () => void

  build() {
    Row(){
      if(this.item.finished){
        Text(this.item.name)
          .finishedTask()
      }else{
        Text(this.item.name)
      }
      Checkbox()
        .select(this.item.finished)
        .onChange(val => {
          // 1.更新当前任务状态
          this.item.finished = val
          // 2.更新已完成任务数量
          this.onTaskChange()
        })
    }
    .card()
    .justifyContent(FlexAlign.SpaceBetween)
  }
}
// TaskList
@Component
struct TaskList {
  // 总任务数量
  @Consume stat: StatInfo
  // 任务数组
  @State tasks: Task[] = []

  handleTaskChange(){
    // 1.更新任务总数量
    this.stat.totalTask = this.tasks.length
    // 2.更新已完成任务数量
    this.stat.finishTask = this.tasks.filter(item => item.finished).length
  }

  build() {
    Column(){
      // 2.新增任务按钮
      Button('新增任务')
        .width(200)
        .margin({bottom: 10})
        .onClick(() => {
          // 1.新增任务数据
          this.tasks.push(new Task())
          // 2.更新任务总数量
          this.handleTaskChange()
        })

      // 3.任务列表
      List({space: 10}){
        ForEach(
          this.tasks,
          (item: Task, index) => {
            ListItem(){
              // 方法传给子组件,注意要this绑定
              TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})
            }
            .swipeAction({end: this.DeleteButton(index)})
          }
        )
      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder DeleteButton(index: number){
    Button(){
      Image($r('app.media.ic_public_delete_filled'))
        .fillColor(Color.White)
        .width(20)
    }
    .width(40)
    .height(40)
    .type(ButtonType.Circle)
    .backgroundColor(Color.Red)
    .margin(5)
    .onClick(() => {
      this.tasks.splice(index, 1)
      this.handleTaskChange()
    })
  }

}

# arkts 路由

export default class RouterInfo{
  // 页面路径
  url: string
  // 页面标题
  title: string

  constructor(url: string, title: string) {
    this.url = url;
    this.title = title
  }
}

const routers: RouterInfo[] = [
  new RouterInfo('pages/ImagePage', '图片查看案例'),
  new RouterInfo('pages/ItemPage', '商品列表案例'),
  new RouterInfo('pages/StatePage', 'Jack和他的女友案例'),
  new RouterInfo('pages/PropPage', '任务列表案例'),
]

import router from '@ohos.router'
import RouterInfo from '../viewmodel/RouterInfo'

@Component
export default struct RouterItem{
  r: RouterInfo
  i: number
  @Consume fontSize: number

  build(){
    Row(){
      Text(this.i + '.')
        .fontSize(this.fontSize)
        .fontColor(Color.White)
      Blank()
      Text(this.r.title)
        .fontSize(this.fontSize)
        .fontColor(Color.White)
    }
    .width('90%')
    .padding(12)
    .backgroundColor('#38f')
    .borderRadius(20)
    .shadow({radius: 6, color: '#4F000000', offsetX: 2, offsetY: 4})
    .onClick(() => {
      // router跳转
      router.pushUrl(
        {
          url: this.r.url,
          params: {id: this.i}
        },
        router.RouterMode.Single,
        err => {
          if(err){
            console.log(`路由失败,errCode: ${err.code} errMsg:${err.message}`)
          }
        }
      )
    })
  }
}

# arkts 动画

属性动画

Image(this.src)
          .position({x: this.fishX - 20, y: this.fishY - 20})
          .rotate({angle:this.angle, centerX: '50%', centerY: '50%'})
          .width(40)
          .height(40)
          .animation({duration: 500, curve: Curve.Smooth})
 Button('←').backgroundColor('#20101010')
            .onClick(() => {
               this.fishX -= 20
               this.src = $r('app.media.fish_rev')
            })

显式动画

  // .animation({duration: 500, curve: Curve.Smooth})
 Button('←').backgroundColor('#20101010')
      .onClick(() => {
        animateTo(
          {duration: 500},
          () => {
            this.fishX -= 20
            this.src = $r('app.media.fish_rev')
          }
        )
      })
      Button('开始游戏')
            .onClick(() => {
              animateTo(
                {duration: 1000},
                () => {
                  // 点击后显示小鱼
                  this.isBegin = true
                }
              )
            })
        }else{
          // 小鱼图片
          Image(this.src)
            .position({x: this.fishX - 20, y: this.fishY - 20})
            .rotate({angle:this.angle, centerX: '50%', centerY: '50%'})
            .width(40)
            .height(40)
            //.animation({duration: 500, curve: Curve.Smooth})
            .transition({
              type: TransitionType.Insert,
              opacity: 0,
              translate: {x: -250}
            })
        }

# stage模型

应用配置文件

# UIAbility生命周期

# arkts页面及组件生命周期

三个页面周期钩子只在@entry修饰的类才生效

import { Header } from '../common/components/CommonComponents'

@Entry
@Component
struct LifeCircle {
  @State show: boolean = false
  @State arr: string[] = []


  tag: string = 'Life Circle Page 1'

  aboutToAppear(){
    console.log(this.tag, 'about to appear')
  }

  onPageShow(){
    console.log(this.tag, 'on page show')
  }

  onBackPress(){
    console.log(this.tag, 'on back press')
  }

  onPageHide(){
    console.log(this.tag, 'on page hide')
  }

  aboutToDisappear(){
    console.log(this.tag, 'about to disappear')
  }

  build() {
    Column({space: 20}) {
      // 顶部标题
      Header({ title: '测试组件生命周期' })

      // 按钮
      Button('切换显示')
        .onClick(() => this.show = !this.show)

      Row(){
        if(this.show){
          MyText({message: 'Hello World!'})
        }
      }
      .height(30)

      // 新增按钮
      Button('新增数据')
        .onClick(() => this.arr.push('user_' + this.arr.length))
      ForEach(this.arr, (item, index) => {
        MyText({message: item, index: index, delete: this.delete.bind(this)})
      })
    }
    .width('100%')
    .height('100%')
  }

  delete(index: number = -1){
    if(index > -1){
      this.arr.splice(index, 1)
    }
  }
}

@Component
struct MyText {
  message: string
  index?: number
  delete?: (index: number) => void


  tag: string = 'MyText Page'

  aboutToAppear(){
    console.log(this.tag, 'about to appear')
  }

  onPageShow(){
    // 不会执行
    console.log(this.tag, 'on page show')
  }

  onBackPress(){
    // 不会执行
    console.log(this.tag, 'on back press')
  }

  onPageHide(){
    // 不会执行
    console.log(this.tag, 'on page hide')
  }

  aboutToDisappear(){
    console.log(this.tag, 'about to disappear')
  }


  build() {
    Row(){
      Text(this.message)
        .fontSize(20)
      if(this.delete){
        Image($r('app.media.ic_public_delete'))
          .width(20)
          .onClick(() => this.delete(this.index))
      }
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }
}

push和replace走的生命周期钩子是不同的,push是会隐藏,而replace则直接销毁

import router from '@ohos.router'
@Entry
@Component
struct TestPage1 {
  @State show: boolean = false
  tag: string = 'TestPage1'

  aboutToAppear(){
    console.log(this.tag, 'about to appear')
  }

  onPageShow(){
    console.log(this.tag, 'on page show')
  }

  onBackPress(){
    console.log(this.tag, 'on back press')
  }

  onPageHide(){
    console.log(this.tag, 'on page hide')
  }

  aboutToDisappear(){
    console.log(this.tag, 'about to disappear')
  }

  build() {
    Row() {
      Column({space: 20}) {
        Text(this.tag)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)

        Row(){
          Button('push page2')
            .onClick(() => {
              router.pushUrl({
                url: 'pages/TestPage2'
              })
            })
          Button('replace page2')
            .onClick(() => {
              router.replaceUrl({
                url: 'pages/TestPage2'
              })
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
      }
      .width('100%')
    }
    .height('100%')
  }
}

# arkts 启动模式

创建新的ability,注意windowStage.loadContent的地址

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class DocumentAbility extends UIAbility {
  onCreate(want, launchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy() {
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/DocumentPage', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

  onWindowStageDestroy() {
    // Main window is destroyed, release UI related resources
  }

  onForeground() {
    // Ability has brought to foreground
  }

  onBackground() {
    // Ability has back to background
  }
}
// DocumentListPage.ets
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
import { Header } from '../common/components/CommonComponents'
@Entry
@Component
struct DocumentListPage {
  private index: number = 1
  @State docs: number[] = []

  private context = getContext(this) as common.UIAbilityContext

  build() {
    Row() {
      Column({space: 10}) {
        Header({title: '文档列表'})
        Divider()
        Button('新建文档')
          .onClick(() => {
            // 添加文档
            this.docs.push(this.index)
            // 跳转到文档编辑的 UIAbility

            // 跳转的目的地want
            let want: Want = {
              deviceId: '',
              bundleName: 'com.example.myapplication',
              moduleName: 'entry',
              abilityName: 'DocumentAbility',// 执行要跳转的ability的name
              parameters: {
                instanceKey: 'idx_' + this.index++
              }
            }
            // 跳转
            this.context.startAbility(want)
          })
        ForEach(this.docs, id => {
          Row({space:10}){
            Image($r('app.media.ic_files_doc'))
              .width(20)
            Text(`文档${id}`)
              .fontSize(18)
              .onClick(() => {
                // 跳转到文档编辑的 UIAbility

                // 跳转的目的地want
                let want: Want = {
                  deviceId: '',
                  bundleName: 'com.example.myapplication',
                  moduleName: 'entry',
                  abilityName: 'DocumentAbility',
                  parameters: {
                    instanceKey: 'idx_' + id
                  }
                }
                // 跳转
                this.context.startAbility(want)
              })
          }
          .width('100%')
        })
      }
      .width('100%')
      .height('100%')
      .padding(20)
    }
  }
}

// DocumentPage.ets
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'

@Entry
@Component
struct DocumentPage {
  private context = getContext(this) as common.UIAbilityContext
  @State editTitle: boolean = true
  @State title: string = '标题'
  @State content: string = ''

  build() {
    Row() {
      Column({ space: 10 }) {
        Row({ space: 10 }) {
          Image($r('app.media.ic_public_back'))
            .width(30)
            .onClick(() => {
              // 跳转到文档列表的 UIAbility
              let want: Want = {
                deviceId: '',
                bundleName: 'com.example.myapplication',
                moduleName: 'entry',
                abilityName: 'EntryAbility'
              }
              // 跳转
              this.context.startAbility(want)
            })
          if (!this.editTitle) {
            Text(this.title)
              .fontSize(30)
              .fontWeight(FontWeight.Bold)
              .onClick(() => this.editTitle = true)
          } else {
            TextInput({
              placeholder: '请输入标题',
              text: this.title
            })
              .layoutWeight(1)
              .onChange(val => this.title = val)
            Button('确定')
              .onClick(() => this.editTitle = false)
          }
        }
        .width('100%')

        Divider()
        TextArea({
          placeholder: '请输入文档内容',
          text: this.content
        })
          .layoutWeight(1)
          .onChange(val => this.content = val)
      }
      .padding(10)
      .width('100%')
      .height('100%')
    }
  }
}

want跳转需要一个新stage,在目录下创建一个新的自定义stage

// entry\src\main\ets\myabilitystage\MyAbilityStage.ts
import AbilityStage from '@ohos.app.ability.AbilityStage';
import Want from '@ohos.app.ability.Want';

export default class MyAbilityStage extends AbilityStage {
  onAcceptWant(want: Want): string {
    if(want.abilityName === 'DocumentAbility'){
      return `DocAbilityInstance_${want.parameters.instanceKey}`
    }
    return ''
  }
}

module.json5:新创建的ability会自动导入,但是新建的stage需要配置srcEntry,需要手动导入

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "name": "entry",
    "type": "entry",
    // 导入新的stage
    "srcEntry": './ets/myabilitystage/MyAbilityStage.ts',
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:ic_main",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "launchType": "standard",
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      },
      // 新的ability的配置
      {
        "name": "DocumentAbility",
        "srcEntry": "./ets/documentability/DocumentAbility.ts",
        "description": "$string:DocumentAbility_desc",
        "icon": "$media:icon",
        "label": "$string:DocumentAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "launchType": "specified"// 启动模式字段
      }
    ]
  }
}

# arkts 网络请求

可用原生http也可以使用第三方库axios

// import http from '@ohos.net.http';
import ShopInfo from '../viewmodel/ShopInfo';
import axios from '@ohos/axios'

class ShopModel{
  baseURL: string = 'http://localhost:3000'
  pageNo: number = 1

  getShopList(): Promise<ShopInfo[]>{
    return new Promise((resolve, reject) => {
      axios.get(
        `${this.baseURL}/shops`,
        {
          params: {pageNo: this.pageNo, pageSize: 3}
        }
      )
        .then(resp => {
          if(resp.status === 200){
            // 查询成功
            console.log('查询商铺成功!', JSON.stringify(resp.data))
            resolve(resp.data)
          }else{
            console.log('查询商铺信息失败!error:', JSON.stringify(resp))
            reject('查询商铺失败')
          }
        })
        .catch(error => {
          console.log('查询商铺信息失败!error:', JSON.stringify(error))
          reject('查询商铺失败')
        })
    })
  }

  /*getShopList(): Promise<ShopInfo[]>{
    return new Promise((resolve, reject) => {
      // 1.创建http的请求对象
      let httpRequest = http.createHttp()
      // 2.发送请求
      httpRequest.request(
        `${this.baseURL}/shops?pageNo=${this.pageNo}&pageSize=3`,
        {
          method: http.RequestMethod.GET
        }
      )
        .then(resp => {
          if(resp.responseCode === 200){
            // 查询成功
            console.log('查询商铺成功!', resp.result)
            resolve(JSON.parse(resp.result.toString()))
          }else{
            console.log('查询商铺信息失败!error:', JSON.stringify(resp))
            reject('查询商铺失败')
          }
        })
        .catch(error => {
          console.log('查询商铺信息失败!error:', JSON.stringify(error))
          reject('查询商铺失败')
        })
    })
  }*/
}

const shopModel = new ShopModel();

export default shopModel as ShopModel;

# arkts中使用axios

# ohpm

在官网或者编译器自行安装

OHPM_HOME:对应目录(C:\Users\wei\AppData\Local\Huawei\ohpm)
path:%OHPM_HOME%\bin
C:\Users\wei\AppData\Local\Huawei\ohpm\bin 
执行./init.bat
重启cmd
查看 ohmp -v

# 数据持久化之preferences首选项配置

// 处理数据持久化,首选项类配置
import preferences from '@ohos.data.preferences';

class PreferencesUtil{

  prefMap: Map<string, preferences.Preferences> = new Map()

  async loadPreference(context, name: string){
    try { // 加载preferences
      let pref = await preferences.getPreferences(context, name)
      this.prefMap.set(name, pref)
      console.log('testTag', `加载Preferences[${name}]成功`)
    } catch (e) {
      console.log('testTag', `加载Preferences[${name}]失败`, JSON.stringify(e))
    }
  }

  async putPreferenceValue(name: string, key: string, value: preferences.ValueType){
    if (!this.prefMap.has(name)) {
      console.log('testTag', `Preferences[${name}]尚未初始化!`)
      return
    }
    try {
      let pref = this.prefMap.get(name)
      // 写入数据
      await pref.put(key, value)
      // 刷盘
      await pref.flush()
      console.log('testTag', `保存Preferences[${name}.${key} = ${value}]成功`)
    } catch (e) {
      console.log('testTag', `保存Preferences[${name}.${key} = ${value}]失败`, JSON.stringify(e))
    }
  }

  async getPreferenceValue(name: string, key: string, defaultValue: preferences.ValueType){
    if (!this.prefMap.has(name)) {
      console.log('testTag', `Preferences[${name}]尚未初始化!`)
      return
    }
    try {
      let pref = this.prefMap.get(name)
      // 读数据
      let value = await pref.get(key, defaultValue)
      console.log('testTag', `读取Preferences[${name}.${key} = ${value}]成功`)
      return value
    } catch (e) {
      console.log('testTag', `读取Preferences[${name}.${key} ]失败`, JSON.stringify(e))
    }
  }
}

const preferencesUtil = new PreferencesUtil()

export default preferencesUtil as PreferencesUtil
// EntryAbility
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';;
import PreferencesUtil from '../common/util/PreferencesUtil'
import TaskModel from '../model/TaskModel'

export default class EntryAbility extends UIAbility {
  async onCreate(want, launchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate running');
    // 加载Preferences
    await PreferencesUtil.loadPreference(this.context, 'MyPreferences')
    // 初始化任务表
    TaskModel.initTaskDB(this.context)
  }
  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.on('windowStageEvent', () =>{})
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }
}
 // 写入Preferences
  PreferencesUtil.putPreferenceValue('MyPreferences', 'IndexFontSize', val)
 // 获取
  async aboutToAppear(){
    this.fontSize = await PreferencesUtil.getPreferenceValue('MyPreferences', 'IndexFontSize', 16) as number
  }

# arkts数据持久化之关系型数据库

//TaskInfo.ets
@Observed
export default class TaskInfo{
  id: number
  // 任务名称
  name: string
  // 任务状态:是否完成
  finished: boolean

  constructor(id: number, name: string) {
    this.id = id
    this.name = name
    this.finished = false
  }
}
// TaskModel.ets,因为要引用ets文件,所以这里它也需要时ets
import relationalStore from '@ohos.data.relationalStore';
import TaskInfo from '../viewmodel/TaskInfo';

class TaskModel {

  private rdbStore: relationalStore.RdbStore
  private tableName: string = 'TASK'

  /**
   * 初始化任务表
   */
  initTaskDB(context){
    // 1.rdb配置
    const config = {
      name: 'MyApplication.db',//数据库名
      securityLevel: relationalStore.SecurityLevel.S1// 安全等级
    }
    // 2.初始化SQL语句
    const sql = `CREATE TABLE IF NOT EXISTS TASK (
                  ID INTEGER PRIMARY KEY AUTOINCREMENT,
                  NAME TEXT NOT NULL,
                  FINISHED bit
                 )`
    // 3.获取rdb
    relationalStore.getRdbStore(context, config, (err, rdbStore) => {
      if(err){
        console.log('testTag', '获取rdbStore失败!')
        return
      }
      // 执行Sql
      rdbStore.executeSql(sql)
      console.log('testTag', '创建task表成功!')
      // 保存rdbStore
      this.rdbStore = rdbStore
    })
  }

  /**
   * 查询任务列表
   */
  async getTaskList(){
    // 1.构建查询条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    // 2.查询
    let result = await this.rdbStore.query(predicates, ['ID', 'NAME', 'FINISHED'])
    // 3.解析查询结果
    // 3.1.定义一个数组,组装最终的查询结果
    let tasks: TaskInfo[] = []
    // 3.2.遍历封装
    while(!result.isAtLastRow){
      // 3.3.指针移动到下一行
      result.goToNextRow()
      // 3.4.获取数据
      let id = result.getLong(result.getColumnIndex('ID'))
      let name = result.getString(result.getColumnIndex('NAME'))
      let finished = result.getLong(result.getColumnIndex('FINISHED'))
      // 3.5.封装到数组
      tasks.push({id, name, finished: !!finished})
    }
    console.log('testTag', '查询到数据:', JSON.stringify(tasks))
    return tasks
  }

  /**
   * 添加一个新的任务
   * @param name 任务名称
   * @returns 任务id
   */
  addTask(name: string): Promise<number>{
    return this.rdbStore.insert(this.tableName, {name, finished: false})
  }

  /**
   * 根据id更新任务状态
   * @param id 任务id
   * @param finished 任务是否完成
   */
  updateTaskStatus(id: number, finished: boolean) {
    // 1.要更新的数据
    let data = {finished}
    // 2.更新的条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 3.更新操作
    return this.rdbStore.update(data, predicates)
  }

  /**
   * 根据id删除任务
   * @param id 任务id
   */
  deleteTaskById(id: number){
    // 1.删除的条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 2.删除操作
    return this.rdbStore.delete(predicates)
  }
}

let taskModel = new TaskModel();

export default taskModel as TaskModel;
// EntryAbility,完整配置参见首选项
import UIAbility from '@ohos.app.ability.UIAbility';
import TaskModel from '../model/TaskModel'

export default class EntryAbility extends UIAbility {
  async onCreate(want, launchParam) {
    // 初始化任务表
    TaskModel.initTaskDB(this.context)
  }
}
 // 1.新增任务
  TaskModel.addTask(name)
    .then(id => {
      console.log('testTag', '处理新增任务: ', name)
      // 回显到数组页面
      this.tasks.push(new TaskInfo(id, name))
      // 2.更新任务完成状态
      this.handleTaskChange()
      // 3.关闭对话框
      this.dialogController.close()
    })
    .catch(error => console.log('testTag', '新增任务失败:', name, JSON.stringify(error)))

// 删除任务
    TaskModel.deleteTaskById(id)
      .then(() => {
        this.tasks.splice(index, 1)
        console.log('testTag', `尝试删除任务,index: ${index}`)
      })
      .catch(error => console.log('testTag', '删除任务失败,id = ', id, JSON.stringify(error)))

# 鸿蒙arkts通知

import notify from '@ohos.notificationManager'
import image from '@ohos.multimedia.image'
import DownloadCard from '../views/notification/DownloadCard'
import { Header } from '../common/components/CommonComponents'

@Entry
@Component
struct NotificationPage {
  // 全局任务id
  idx: number = 100
  // 图象
  pixel: PixelMap

  async aboutToAppear() {
    // 获取资源管理器
    let rm = getContext(this).resourceManager;
    // 读取图片
    let file = await rm.getMediaContent($r('app.media.watchGT4'))
    // 创建PixelMap
    image.createImageSource(file.buffer).createPixelMap()
      .then(value => this.pixel = value)
      .catch(reason => console.log('testTag', '加载图片异常', JSON.stringify(reason)))
  }

  build() {
    Column({space: 20}) {
      Header({title: '通知功能'})

      Button(`发送normalText通知`)
        .onClick(() => this.publishNormalTextNotification())
      Button(`发送longText通知`)
        .onClick(() => this.publishLongTextNotification())
      Button(`发送multiLine通知`)
        .onClick(() => this.publishMultiLineNotification())
      Button(`发送Picture通知`)
        .onClick(() => this.publishPictureNotification())

      // 下载功能卡片
      DownloadCard()
    }
    .width('100%')
    .height('100%')
    .padding(5)
    .backgroundColor('#f1f2f3')
  }

  publishNormalTextNotification() {
    let request: notify.NotificationRequest = {
      id: this.idx++,
      content: {
        contentType: notify.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: '通知标题' + this.idx,
          text: '通知内容详情',
          additionalText: '通知附加内容'
        }
      },
      showDeliveryTime: true,
      deliveryTime: new Date().getTime(),
      groupName: 'wechat',
      slotType: notify.SlotType.SOCIAL_COMMUNICATION
    }
    this.publish(request)
  }

  publishLongTextNotification() {
    let request: notify.NotificationRequest = {
      id: this.idx++,
      content: {
        contentType: notify.ContentType.NOTIFICATION_CONTENT_LONG_TEXT,
        longText: {
          title: '通知标题' + this.idx,
          text: '通知内容详情',
          additionalText: '通知附加内容',
          longText: '通知中的长文本,我很长,我很长,我很长,我很长,我很长,我很长,我很长',
          briefText: '通知概要和总结',
          expandedTitle: '通知展开时的标题' + this.idx
        }
      }
    }
    this.publish(request)
  }

  publishMultiLineNotification() {
    let request: notify.NotificationRequest = {
      id: this.idx++,
      content: {
        contentType: notify.ContentType.NOTIFICATION_CONTENT_MULTILINE,
        multiLine: {
          title: '通知标题' + this.idx,
          text: '通知内容详情',
          additionalText: '通知附加内容',
          briefText: '通知概要和总结',
          longTitle: '展开时的标题,我很宽,我很宽,我很宽',
          lines: [
            '第一行',
            '第二行',
            '第三行',
            '第四行',
          ]
        }
      }
    }
    this.publish(request)
  }

  publishPictureNotification() {
    let request: notify.NotificationRequest = {
      id: this.idx++,
      content: {
        contentType: notify.ContentType.NOTIFICATION_CONTENT_PICTURE,
        picture: {
          title: '通知标题' + this.idx,
          text: '通知内容详情',
          additionalText: '通知附加内容',
          briefText: '通知概要和总结',
          expandedTitle: '展开后标题' + this.idx,
          picture: this.pixel
        }
      }
    }
    this.publish(request)
  }

  private publish(request: notify.NotificationRequest) {
    notify.publish(request)
      .then(() => console.log('notify test', '发送通知成功'))
      .then(reason => console.log('notify test', '发送通知失败', JSON.stringify(reason)))
  }
}

# arkts进度条通知

import notify from '@ohos.notificationManager'
import wantAgent, { WantAgent } from '@ohos.app.ability.wantAgent'
import promptAction from '@ohos.promptAction'

enum DownloadState {
  NOT_BEGIN = '未开始',
  DOWNLOADING = '下载中',
  PAUSE = '已暂停',
  FINISHED = '已完成',
}

@Component
export default struct DownloadCard {
  // 下载进度
  @State progressValue: number = 0
  progressMaxValue: number = 100
  // 任务状态
  @State state: DownloadState = DownloadState.NOT_BEGIN

  // 下载的文件名
  filename: string = '圣诞星.mp4'

  // 模拟下载的任务的id
  taskId: number = -1

  // 通知id
  notificationId: number = 999

  isSupport: boolean = false

  wantAgentInstance: WantAgent

  async aboutToAppear(){
    // 1.判断当前系统是否支持进度条模板
    this.isSupport = await notify.isSupportTemplate('downloadTemplate')
    // 2.创建拉取当前应用的行为意图
    // 2.1.创建wantInfo信息
    let wantInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: 'com.example.myapplication',
          abilityName: 'EntryAbility',
        }
      ],
      requestCode: 0,
      operationType: wantAgent.OperationType.START_ABILITY,
      wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]
    }
    // 2.2.创建wantAgent实例
    this.wantAgentInstance = await wantAgent.getWantAgent(wantInfo)
  }

  build() {
    Row({ space: 10 }) {
      Image($r('app.media.ic_files_video')).width(50)
      Column({ space: 5 }) {
        Row() {
          Text(this.filename)
          Text(`${this.progressValue}%`).fontColor('#c1c2c1')
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)

        Progress({
          value: this.progressValue,
          total: this.progressMaxValue,
        })

        Row({ space: 5 }) {
          Text(`${(this.progressValue * 0.43).toFixed(2)}MB`)
            .fontSize(14).fontColor('#c1c2c1')
          Blank()
          if (this.state === DownloadState.NOT_BEGIN) {
            Button('开始').downloadButton()
              .onClick(() => this.download())

          } else if (this.state === DownloadState.DOWNLOADING) {
            Button('取消').downloadButton().backgroundColor('#d1d2d3')
              .onClick(() => this.cancel())

            Button('暂停').downloadButton()
              .onClick(() => this.pause())

          } else if (this.state === DownloadState.PAUSE) {
            Button('取消').downloadButton().backgroundColor('#d1d2d3')
              .onClick(() => this.cancel())

            Button('继续').downloadButton()
              .onClick(() => this.download())
          } else {
            Button('打开').downloadButton()
              .onClick(() => this.open())
          }
        }.width('100%')
      }
      .layoutWeight(1)
    }
    .width('100%')
    .borderRadius(20)
    .padding(15)
    .backgroundColor(Color.White)
  }

  cancel() {
    // 取消定时任务
    if(this.taskId > 0){
      clearInterval(this.taskId);
      this.taskId = -1
    }
    // 清理下载任务进度
    this.progressValue = 0
    // 标记任务状态:未开始
    this.state = DownloadState.NOT_BEGIN
    // 取消通知
    notify.cancel(this.notificationId)
  }

  download() {
    // 清理旧任务
    if(this.taskId > 0){
      clearInterval(this.taskId);
    }
    // 开启定时任务,模拟下载
    this.taskId = setInterval(() => {
      // 判断任务进度是否达到100
      if(this.progressValue >= 100){
        // 任务完成了,应该取消定时任务
        clearInterval(this.taskId)
        this.taskId = -1
        // 并且标记任务状态为已完成
        this.state = DownloadState.FINISHED
        // 发送通知
        this.publishDownloadNotification()
        return
      }
      // 模拟任务进度变更
      this.progressValue += 2
      // 发送通知
      this.publishDownloadNotification()
    }, 500)
    // 标记任务状态:下载中
    this.state = DownloadState.DOWNLOADING
  }

  pause() {
    // 取消定时任务
    if(this.taskId > 0){
      clearInterval(this.taskId);
      this.taskId = -1
    }
    // 标记任务状态:已暂停
    this.state = DownloadState.PAUSE
    // 发送通知
    this.publishDownloadNotification()
  }

  open() {
    promptAction.showToast({
      message: '功能未实现'
    })
  }

  publishDownloadNotification(){
    // 1.判断当前系统是否支持进度条模板
    if(!this.isSupport){
      // 当前系统不支持进度条模板
      return
    }
    // 2.准备进度条模板的参数
    let template = {
      name: 'downloadTemplate',
      data: {
        progressValue: this.progressValue,
        progressMaxValue: this.progressMaxValue
      }
    }
    let request: notify.NotificationRequest = {
      id: this.notificationId,
      template: template,
      wantAgent: this.wantAgentInstance,
      content: {
        contentType: notify.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: this.filename + ':  ' + this.state,
          text: '',
          additionalText: this.progressValue + '%'
        }
      }
    }
    // 3.发送通知
    notify.publish(request)
      .then(() => console.log('test', '通知发送成功'))
      .catch(reason => console.log('test', '通知发送失败!', JSON.stringify(reason)))
  }
}

@Extend(Button) function downloadButton() {
  .width(75).height(28).fontSize(14)
}

# arkts 行为意图

代码参见进度条通知中的内容

最后更新: 6/25/2024, 6:44:53 PM