【HarmonyOS之旅】ArkTS语法(一)
目录
1 -> 基本UI描述
1.1 -> 基本概念
1.2 -> UI描述规范
1.2.1 -> 无参数构造配置
1.2.2 -> 必选参数构造配置
1.2.3 -> 属性配置
1.2.4 -> 事件配置
1.2.5 -> 子组件配置
2 -> 状态管理
2.1 -> 基本概念
2.2 -> 页面级变量的状态管理
2.2.1 -> @State
2.2.2 -> @Prop
2.2.3 -> @Link
2.2.4 -> @Observed和ObjectLink数据管理
2.2.5 -> @Consume和@Provide
2.2.6 -> @Watch
2.3 -> 应用级变量的状态管理
2.3.1 -> AppStorage
2.3.2 -> PersistentStorage
2.3.3 -> Environment
1 -> 基本UI描述
ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结构,构成一个自定义组件。
1.1 -> 基本概念
-
struct:自定义组件可以基于struct实现,不能有继承关系,对于struct的实例化,可以省略new。
-
装饰器:装饰器给被装饰的对象赋予某一种能力,其不仅可以装饰类或结构体,还可以装饰类的属性。多个装饰器可以叠加到目标元素上,定义在同一行中或者分开多行,推荐分开多行定义。
@Entry
@Component
struct MyComponent {
}
- build函数:自定义组件必须定义build函数,并且禁止自定义构造函数。build函数满足Builder构造器接口定义,用于定义组件的声明式UI描述。
interface Builder {build: () => void
}
- @Component:装饰struct,结构体在装饰后具有基于组件的能力,需要实现build方法来创建UI。
- @Entry:装饰struct,组件被装饰后作为页面的入口,页面加载时被渲染显示。
- @Preview:装饰struct,用@Preview装饰的自定义组件可以在DevEco Studio的预览器上进行实时预览,加载页面时,将创建并显示@Preview装饰的自定义组件。
说明:
在单个源文件中,最多可以使用10个@Preview装饰自定义组件。
- 链式调用:以“.”链式调用的方法配置UI组件的属性方法、事件方法等。
1.2 -> UI描述规范
1.2.1 -> 无参数构造配置
如果组件的接口定义中不包括必选构造参数,组件后面的“()”中不需要配置任何内容。例如,Divider组件不包含构造参数。
Column() {Text('item 1')Divider()Text('item 2')
}
1.2.2 -> 必选参数构造配置
如果组件的接口定义中包含必须按构参数,则在组件后面的“()”中必须配置相应参数,参数可以使用常量进行赋值。
例如:
- Image组件的必选参数src:
Image('https://xyz/test.jpg')
- Text组件的必选参数content:
Text('test')
变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。
例如,设置变量或表达式来构造Image和Text组件的参数:
Image(this.imagePath)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
1.2.3 -> 属性配置
使用属性方法配置组件的属性,属性方法紧随组件,并用“.”运算符连接。
- 配置Text组件的字体大小属性:
Text('test').fontSize(12)
- 使用“.”运算符进行链式调用并同时配置组件的多个属性,如下所示:
Image('test.jpg').alt('error.jpg') .width(100) .height(100)
- 除了直接传递常量参数外,还可以传递变量或表达式,如下所示:
Text('hello').fontSize(this.size)
Image('test.jpg').width(this.count % 2 === 0 ? 100 : 200) .height(this.offset + 100)
- 对于系统内置组件,框架还为其属性预定义了一些枚举类型供开发人员调用,枚举类型可以作为参数传递,且必须满足参数类型要求。
例如,可以按以下方式配置Text组件的颜色和字体属性:
Text(this.message).id('One Piece').fontColor(Color.Blue).fontSize(50).fontWeight(FontWeight.Bold)
1.2.4 -> 事件配置
通过事件方法可以配置组件支持的事件,事件方法紧随组件,并用“.”运算符连接。
- 使用lambda表达式配置组件的事件方法:
Button('add counter').onClick(() => {this.counter += 2})
- 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this引用包含的组件:
Button('add counter').onClick(function () {this.counter += 2}.bind(this))
- 使用组件的成员函数配置组件的事件方法:
myClickHandler(): void {this.counter += 2
}...Button('add counter').onClick(this.myClickHandler)
1.2.5 -> 子组件配置
对于支持子组件配置的组件,例如容器组件,在“{……}”里为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。
- 以下是简单的Column示例:
Column() {Text('Hello').fontSize(100)Divider()Text(this.myText).fontSize(100).fontColor(Color.Red)
}
- 容器组件之间也可以互相嵌套,实现相对复杂的多级嵌套效果:
Column() {Row() {Image('test1.jpg').width(100).height(100)Button('click +1').onClick(() => {console.info('+1 clicked!')})}Divider()Row() {Image('test2.jpg').width(100).height(100)Button('click +2').onClick(() => {console.info('+2 clicked!')})}Divider()Row() {Image('test3.jpg').width(100).height(100)Button('click +3').onClick(() => {console.info('+3 clicked!')})}
}
2 -> 状态管理
2.1 -> 基本概念
ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是全局范围内的传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。可以灵活的利用这些能力来实现数据和UI的联动。
2.2 -> 页面级变量的状态管理
@State、@Prop、@Link、@Provide、@Consume、@ObjectLink、@Observed和@Watch用于管理页面级变量的状态。
装饰器 | 装饰内容 | 说明 |
@State | 基本数据类型、类、数组 | 修饰的状态数据被修改时会触发组件的build方法进行UI界面更新。 |
@Prop | 基本数据类型 | 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,更新当前组件的UI。 |
@Link | 基本数据类型、类、数组 | 父子组件之间的双向数据绑定,父组件的内部状态数据作为数据源,任何一方所做的修改都会反映给另一方。 |
@Observed | 类 | @Observed应用于类,表示该类中的数据变更被UI页面管理。 |
@ObjectLink | 被@Observed所装饰类的对象 | 装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会更新UI。 |
@Consume | 基本数据类型、类、数组 | @Consume装饰的变量在感知到@Provide装饰的变量更新后,会触发当前自定义组件的重新渲染。 |
@Provide | 基本数据类型、类、数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。 |
2.2.1 -> @State
@State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
@State状态数据具有以下特征:
- 支持多种类型数据:支持class、number、boolean、string强类型数据的值类型和引用类型,以及这些强类型构成的数组,即Array<class>、Array<string>、Array<boolean>、Array<number>。不支持object和any。
- 支持多实例:组件不同实例的内部状态数据独立。
- 内部私有:标记为@State的属性是私有变量,只能在组件内访问。
- 需要本地初始化:必须为所有@State变量分配初始值,变量未初始化可能导致未定义的框架异常行为。
- 创建自定义组件时支持通过状态变量名设置初始值:在创建组件实例时,可以通过变量名显式指定@State状态变量的初始值。
示例:
在下面的示例中:
-
用户定义的组件MyComponent定义了@State状态变量count和title。如果count或title的值发生变化,则执行MyComponent的build方法来重新渲染组件;
-
EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent;
-
创建MyComponent实例时通过变量名给组件内的变量进行初始化,如:
MyComponent({ title: { value: 'Hello World 2' }, count: 7 })
// Test_State.ets
class Model {value: stringconstructor(value: string) {this.value = value}
}@Entry
@Component
struct EntryComponent {build() {Column() {MyComponent({ count: 1, increaseBy: 2 }) // 第1个MyComponent实例MyComponent({ title: { value: 'Hello World 2' }, count: 7 }) // 第2个MyComponent实例}}
}@Component
struct MyComponent {@State title: Model = { value: 'Hello World' }@State count: number = 0private toggle: string = 'Hello World'private increaseBy: number = 1build() {Column() {Text(`${this.title.value}`).fontSize(30)Button('Click to change title').margin(20).onClick(() => {// 修改内部状态变量titlethis.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello ArkUI'})Button(`Click to increase count=${this.count}`).margin(20).onClick(() => {// 修改内部状态变量countthis.count += this.increaseBy})}}
}
2.2.2 -> @Prop
@Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但变量的更改不会通知给父组件,父组件变量的更改会同步到@prop装饰的变量,即@Prop属于单向数据绑定。
@Prop状态数据具有以下特征:
- 支持简单类型:仅支持number、string、boolean等简单数据类型;
- 私有:仅支持组件内访问;
- 支持多个实例:一个组件中可以定义多个标有@Prop的属性;
- 创建自定义组件时将值传递给@Prop变量进行初始化:在创建组件的新实例时,必须初始化所有@Prop变量,不支持在组件内部进行初始化。
示例:
在下面的示例中,当按“+1”或“-1”按钮时,父组件状态发生变化,重新执行build方法,此时将创建一个新的CountDownComponent组件实例。父组件的countDownStartValue状态变量被用于初始化子组件的@Prop变量,当按下子组件的“count - costOfOneAttempt”按钮时,其@Prop变量count将被更改,CountDownComponent重新渲染,但是count值的更改不会影响父组件的countDownStartValue值。
// Test_Prop.ets
@Entry
@Component
struct ParentComponent {@State countDownStartValue: number = 10 // 初始化countDownStartValuebuild() {Column() {Text(`Grant ${this.countDownStartValue} nuggets to play.`).fontSize(18)Button('+1 - Nuggets in New Game').margin(15).onClick(() => {this.countDownStartValue += 1})Button('-1 - Nuggets in New Game').margin(15).onClick(() => {this.countDownStartValue -= 1})// 创建子组件时,必须在构造函数参数中提供其@Prop变量count的初始值,同时初始化常规变量costOfOneAttempt(非Prop变量)CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })}}
}@Component
struct CountDownComponent {@Prop count: numberprivate costOfOneAttempt: numberbuild() {Column() {if (this.count > 0) {Text(`You have ${this.count} Nuggets left`).fontSize(18)} else {Text('Game over!').fontSize(18)}Button('count - costOfOneAttempt').margin(15).onClick(() => {this.count -= this.costOfOneAttempt})}}
}
2.2.3 -> @Link
@Link装饰的变量可以和父组件的@State变量建立双向数据绑定:
- 支持多种类型:@Link支持的数据类型与@State相同,即class、number、string、boolean或这些类型的数组;
- 私有:仅支持组件内访问;
- 单个数据源:父组件中用于初始化子组件@Link变量的必须是@State变量;
- 双向通信:子组件对@Link变量的更改将同步修改父组件中的@State变量;
- 创建自定义组件时需要将变量的引用传递给@Link变量,在创建组件的新实例时,必须使用命名参数初始化所有@Link变量。@Link变量可以使用@State变量或@Link变量的引用进行初始化,@State变量可以通过'$'操作符创建引用。
说明
@Link变量不能在组件内部进行初始化。
简单类型示例:
@Link语义是从'$'操作符引出,即$isPlaying是this.isPlaying内部状态的双向数据绑定。当单击子组件PlayButton中的按钮时,@Link变量更改,PlayButton与父组件中的Text和Button将同时进行刷新,同样地,当点击父组件中的Button修改this.isPlaying时,子组件PlayButton与父组件中的Text和Button也将同时刷新。
// Test_Link.ets
@Entry
@Component
struct Player {@State isPlaying: boolean = falsebuild() {Column() {PlayButton({ buttonPlaying: $isPlaying })Text(`Player is ${this.isPlaying ? '' : 'not'} playing`).fontSize(18)Button('Parent:' + this.isPlaying).margin(15).onClick(() => {this.isPlaying = !this.isPlaying})}}
}@Component
struct PlayButton {@Link buttonPlaying: booleanbuild() {Column() {Button(this.buttonPlaying ? 'pause' : 'play').margin(20).onClick(() => {this.buttonPlaying = !this.buttonPlaying})}}
}
复杂类型示例:
// Test_Link.ets
@Entry
@Component
struct Parent {@State arr: number[] = [1, 2, 3]build() {Column() {Child({ items: $arr })Button('Parent Button: splice').margin(10).onClick(() => {this.arr.splice(0, 1, 60)})ForEach(this.arr, item => {Text(item.toString()).fontSize(18).margin(10)}, item => item.toString())}}
}@Component
struct Child {@Link items: number[]build() {Column() {Button('Child Button1: push').margin(15).onClick(() => {this.items.push(100)})Button('Child Button2: replace whole item').margin(15).onClick(() => {this.items = [100, 200, 300]})}}
}
@Link、@State和@Prop结合使用示例:
下面示例中,ParentView包含ChildA和ChildB两个子组件,ParentView的状态变量counter分别用于初始化ChildA的@Prop变量和ChildB的@Link变量。
- ChildB使用@Link建立双向数据绑定,当ChildB修改counterRef状态变量值时,该更改将同步到ParentView和ChildA共享;
- ChildA使用@Prop建立从ParentView到自身的单向数据绑定,当ChildA修改counterVal状态变量值时,ChildA将重新渲染,但该更改不会传达给ParentView和ChildB。
// Test_Combine.ets
@Entry
@Component
struct ParentView {@State counter: number = 0build() {Column() {ChildA({ counterVal: this.counter })ChildB({ counterRef: $counter })}}
}@Component
struct ChildA {@Prop counterVal: numberbuild() {Button(`ChildA: (${this.counterVal}) + 1`).margin(15).onClick(() => {this.counterVal += 1})}
}@Component
struct ChildB {@Link counterRef: numberbuild() {Button(`ChildB: (${this.counterRef}) + 1`).margin(15).onClick(() => {this.counterRef += 1})}
}
2.2.4 -> @Observed和ObjectLink数据管理
当开发者需要在子组件中针对父组件的一个变量(parent_a)设置双向同步时,开发者可以在父组件中使用@State装饰变量(parent_a),并在子组件中使用@Link装饰对应的变量(child_a)。这样不仅可以实现父组件与单个子组件之间的数据同步,也可以实现父组件与多个子组件之间的数据同步。如下图所示,可以看到,父子组件针对ClassA类型的变量设置了双向同步,那么当子组件1中变量对应的属性c的值变化时,会通知父组件同步变化,而当父组件中属性c的值变化时,会通知所有子组件同步变化。
然而,上述例子是针对某个数据对象进行的整体同步,而当开发者只想针对父组件中某个数据对象的部分信息进行同步时,使用@Link就不能满足要求。如果这些部分信息是一个类对象,就可以使用@ObjectLink配合@Observed来实现,如下图所示。
设置要求
-
@Observed用于类,@ObjectLink用于变量。
-
@ObjectLink装饰的变量类型必须为类(class type)。
- 类要被@Observed装饰器所装饰。
- 不支持简单类型参数,可以使用@Prop进行单向同步。
-
@ObjectLink装饰的变量是不可变的。
- 属性的改动是被允许的,当改动发生时,如果同一个对象被多个@ObjectLink变量所引用,那么所有拥有这些变量的自定义组件都会被通知进行重新渲染。
-
@ObjectLink装饰的变量不可设置默认值。
- 必须让父组件中有一个由@State、@Link、@StorageLink、@Provide或@Consume装饰的变量所参与的TS表达式进行初始化。
-
@ObjectLink装饰的变量是私有变量,只能在组件内访问。
示例
// Test_ObjectLink.ets
// 父组件ViewB中的类对象ClassA与子组件ViewA保持数据同步时,可以使用@ObjectLink和@Observed,绑定该数据对象的父组件和其他子组件同步更新
var nextID: number = 0@Observed
class ClassA {public name: stringpublic c: numberpublic id: numberconstructor(c: number, name: string = 'OK') {this.name = namethis.c = cthis.id = nextID++}
}@Component
struct ViewA {label: string = 'ViewA1'@ObjectLink a: ClassAbuild() {Row() {Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`).onClick(() => {this.a.c += 1})}.margin({ top: 10 })}
}@Entry
@Component
struct ViewB {@State arrA: ClassA[] = [new ClassA(0), new ClassA(0)]build() {Column() {ForEach(this.arrA, (item) => {ViewA({ label: `#${item.id}`, a: item })}, (item) => item.id.toString())ViewA({ label: `this.arrA[first]`, a: this.arrA[0] })ViewA({ label: `this.arrA[last]`, a: this.arrA[this.arrA.length - 1] })Button(`ViewB: reset array`).margin({ top: 10 }).onClick(() => {this.arrA = [new ClassA(0), new ClassA(0)]})Button(`ViewB: push`).margin({ top: 10 }).onClick(() => {this.arrA.push(new ClassA(0))})Button(`ViewB: shift`).margin({ top: 10 }).onClick(() => {this.arrA.shift()})}.width('100%')}
}
2.2.5 -> @Consume和@Provide
@Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。@Consume在感知到@Provide数据的更新后,会触发当前自定义组件的重新渲染。
说明
使用@Provide和@Consume时应避免循环引用导致死循环。
名称 | 说明 |
装饰器参数 | 是一个string类型的常量,用于给装饰的变量起别名。如果规定别名,则提供对应别名的数据更新。如果没有,则使用变量名作为别名。推荐使用@Provide('alias')这种形式。 |
同步机制 | @Provide的变量类似@State,可以修改对应变量进行页面重新渲染。也可以修改@Consume装饰的变量,反向修改@State变量。 |
初始值 | 必须设置初始值。 |
页面重渲染场景 | 触发页面渲染的修改: - 基础类型(boolean,string,number)变量的改变; - @Observed class类型变量及其属性的修改; - 添加,删除,更新数组中的元素。 |
类型 | 说明 |
初始值 | 不可设置默认初始值 |
示例
// Test_consume.ets
@Entry
@Component
struct CompA {@Provide("reviewVote") reviewVotes: number = 0;build() {Column() {CompB()Button(`CompA: ${this.reviewVotes}`).margin(10).onClick(() => {this.reviewVotes += 1;})}}
}@Component
struct CompB {build() {Column() {CompC()}}
}@Component
struct CompC {@Consume("reviewVote") reviewVotes: numberbuild() {Column() {Button(`CompC: ${this.reviewVotes}`).margin(10).onClick(() => {this.reviewVotes += 1})}.width('100%')}
}
2.2.6 -> @Watch
@Watch用于监听状态变量的变化,语法结构为:
@State @Watch("onChanged") count : number = 0
如上所示,给状态变量增加一个@Watch装饰器,通过@Watch注册一个回调方法onChanged, 当状态变量count被改变时, 触发onChanged回调。
装饰器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink所装饰的变量均可以通过@Watch监听其变化。
// Test_Watch.ets
@Entry
@Component
struct CompA {@State @Watch('onBasketUpdated') shopBasket: Array<number> = [7, 12, 47, 3]@State totalPurchase: number = 0@State addPurchase: number = 0aboutToAppear() {this.updateTotal()}updateTotal(): number {let sum = 0;this.shopBasket.forEach((i) => {sum += i})// 计算新的购物篮总价值,如果超过100,则适用折扣this.totalPurchase = (sum < 100) ? sum : 0.9 * sumreturn this.totalPurchase}// shopBasket更改时触发该方法onBasketUpdated(propName: string): void {this.updateTotal()}build() {Column() {Button('add to basket ' + this.addPurchase).margin(15).onClick(() => {this.addPurchase = Math.round(100 * Math.random())this.shopBasket.push(this.addPurchase)})Text(`${this.totalPurchase}`).fontSize(30)}}
}
2.3 -> 应用级变量的状态管理
2.3.1 -> AppStorage
AppStorage是应用程序中的单例对象,由UI框架在应用程序启动时创建,在应用程序退出时销毁,为应用程序范围内的可变状态属性提供中央存储。
AppStorage包含整个应用程序中需要访问的所有状态属性,只要应用程序保持运行,AppStorage就会保存所有属性及属性值,属性值可以通过唯一的键值进行访问。
组件可以通过装饰器将应用程序状态数据与AppStorage进行同步,应用业务逻辑的实现也可以通过接口访问AppStorage。
AppStorage的选择状态属性可以与不同的数据源或数据接收器同步,这些数据源和接收器可以是设备上的本地或远程,并具有不同的功能,如数据持久性。这样的数据源和接收器可以独立于UI在业务逻辑中实现。
默认情况下,AppStorage中的属性是可变的,AppStorage还可使用不可变(只读)属性。
@StorageLink装饰器
组件通过使用@StorageLink(key)装饰的状态变量,与AppStorage建立双向数据绑定,key为AppStorage中的属性键值。当创建包含@StorageLink的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。在UI组件中对@StorageLink的状态变量所做的更改将同步到AppStorage,并从AppStorage同步到任何其他绑定实例中,如PersistentStorage或其他绑定的UI组件。
@StorageProp装饰器
组件通过使用@StorageProp(key)装饰的状态变量,将与AppStorage建立单向数据绑定,key标识AppStorage中的属性键值。当创建包含@StoageProp的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。AppStorage中的属性值的更改会导致绑定的UI组件进行状态更新。
示例
每次用户单击Count按钮时,this.varA变量值都会增加,此变量与AppStorage中的varA同步。每次用户单击当前语言按钮时,修改AppStorage中的languageCode,此修改会同步给this.lang变量。
// Test_AppStorage.ets@Entry
@Component
struct ComponentA {@StorageLink('varA') varA: number = 2@StorageProp('languageCode') lang: string = 'en'private label: string = 'count'aboutToAppear() {this.label = (this.lang === 'zh') ? '数' : 'Count'}build() {Column(){Row({ space: 20 }) {Button(`${this.label}: ${this.varA}`).onClick(() => {AppStorage.Set<number>('varA', AppStorage.Get<number>('varA') + 1)})Button(`lang: ${this.lang}`).onClick(() => {if (this.lang === 'zh') {AppStorage.Set<string>('languageCode', 'en')} else {AppStorage.Set<string>('languageCode', 'zh')}this.label = (this.lang === 'zh') ? '数' : 'Count'})}.margin({ bottom: 50 })Row(){Button(`更改@StorageLink修饰的变量:${this.varA}`).fontSize(10).onClick(() => {this.varA++})}.margin({ bottom: 50 })Row(){Button(`更改@StorageProp修饰的变量:${this.lang}`).fontSize(10).onClick(() => {this.lang = 'test'})}}}
}
2.3.2 -> PersistentStorage
PersistentStorage类提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
说明
- PersistProp接口使用时,需要保证输入对应的key应当在AppStorage存在。
- DeleteProp接口使用时,只能对本次启动已经link过的数据生效。
// Test_PersistentStorage.ets
PersistentStorage.PersistProp("highScore", "0");@Entry
@Component
struct PersistentComponent {@StorageLink('highScore') highScore: string = '0'@State currentScore: number = 0build() {Column() {if (this.currentScore === Number(this.highScore)) {Text(`new highScore : ${this.highScore}`)}Button() {Text(`goal!, currentScore : ${this.currentScore}`).fontSize(10)}.onClick(() => {this.currentScore++if (this.currentScore > Number(this.highScore)) {this.highScore = this.currentScore.toString()}})}}
}
2.3.3 -> Environment
Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态属性,这些属性描述了应用程序运行的设备环境。Environment及其属性是不可变的,所有属性值类型均为简单类型。如下示例展示了从Environment获取语音环境:
Environment.EnvProp("accessibilityEnabled", "default");
var enable = AppStorage.Get("accessibilityEnabled");
accessibilityEnabled是Environment提供默认系统变量识别符。首先需要将对应系统属性绑定到AppStorage中,再通过AppStorage中的方法或者装饰器访问对应系统的属性数据。
感谢各位大佬支持!!!
互三啦!!!
相关文章:

【HarmonyOS之旅】ArkTS语法(一)
目录 1 -> 基本UI描述 1.1 -> 基本概念 1.2 -> UI描述规范 1.2.1 -> 无参数构造配置 1.2.2 -> 必选参数构造配置 1.2.3 -> 属性配置 1.2.4 -> 事件配置 1.2.5 -> 子组件配置 2 -> 状态管理 2.1 -> 基本概念 2.2 -> 页面级变量的状…...

【畅购电商】项目总结
目录 1. 电商项目架构图 1.1 系统架构 1.2 技术架构 2. 介绍电商项目 2.1 后台和前台、后端和前端 2.2 Vue全家桶包含哪些技术? 2.3 什么是Vuex? 2.4 什么是SSR 2.5 电商模式是什么? 2.6 枚举类 2.7 elasticsearch相关 2.8 gatew…...
python|利用ffmpeg按顺序合并指定目录内的ts文件
前言: 有的时候我们利用爬虫爬取到的ts文件很多,但ts文件只是视频片段,并且这些视频片段是需要按照一定的顺序合并的,通常ts文件合并输出格式为mp4格式 因此,本文介绍利用python,调用ffmpeg来批量的按自己…...

IP属地和所在地不一致什么意思?怎么换成另外一个地方的
在数字化时代,IP地址作为网络设备的唯一标识符,不仅关乎设备间的通信,还涉及到用户的网络身份与位置信息。然而,有时我们会发现,社交媒体或网络平台上显示的IP属地与用户的实际所在地并不一致。这种不一致现象引发了诸…...

Hive其十,优化和数据倾斜
目录 Hive优化 1、开启本地模式 2、explain分析SQL语句 3、修改Fetch操作 4、开启hive的严格模式【提高了安全性】 5、JVM重用 6、分区、分桶以及压缩 7、合理设置map和reduce的数量 合理设置map数量: 设置合理的reducer的个数 8、设置并行执行 9、CBO优…...

matlab reshape permute
1.reshape 将向量按照顺序重新构建 矩阵,新矩阵 先排完第一列, 再第二列… 2.permute 将向量 维度变换...

数据库sql语句单表查询
简单的增删改查操作 select count(*) from user where accountadmin and password123456 select count(*) from user where account"admin" insert into user(account,password) values ("admin","777") update user set password "666&…...
Linux高级--2.4.2 linux TCP 系列操作函数 -- 深层理解
一、操作函数简介 在 Linux 中,TCP(传输控制协议)操作涉及多种系统调用和函数,通常用来创建套接字、连接、发送/接收数据、关闭连接等。以下是一些常用的 TCP 操作函数和它们的简要说明: 1. socket() 函数原型: int…...
科技快讯 | 水滴筹成为民政部指定个人求助网络服务平台;小米超级小爱首次向正式版用户开放;腾讯发布全球首个重症医疗大模型
本地 AI 开发利器,初探微软 Win11 AI Dev Gallery 功能 12月27日,科技媒体Windows Latest报道,微软推出AI Dev Gallery功能,助力开发者集成端侧AI。该功能支持Windows 10/11,提供25个示例模型,涵盖多领域。…...

强化特种作业管理,筑牢安全生产防线
在各类生产经营活动中,特种作业由于其操作的特殊性和高风险性,一直是安全生产管理的重点领域。有效的特种作业管理体系涵盖多个关键方面,从作业人员的资质把控到安全设施的配备维护,再到特种设备的精细管理以及作业流程的严格规范…...
跨语言学习之C++ 和 Python 的赋值操作 (等号“=“) 的区别
C++ 和 Python 的赋值操作(等号 =)在底层机制和表现行为上有显著区别,主要体现在变量与对象的关系、内存管理和对象类型等方面。以下是两者的详细对比: 1. 变量与对象的关系 Python: 变量是对象的引用,赋值操作是将变量指向某个对象的内存地址。多个变量可以指向同一个对…...
【操作系统】如何创建一个守护进程
守护进程(Daemon)是一类在后台运行的特殊进程,它们通常不与任何终端或用户直接交互,而是执行特定的系统任务或等待系统或网络事件的发生。守护进程是操作系统中不可或缺的一部分,它们负责执行各种后台任务,…...

常见显示方案
常见的屏幕 LED段码屏幕 LED点阵屏 本质上是小LED组成的阵列 显示架构为 : 显示内容(MCU内部)————通信接口———对应的LED显示 多使用1640等LED专用驱动 通信方式 两线串行接口(SCLK,DIN)IIC SPI 等…...

USB Hub 检测设备
系列文章目录 xHCI 简单分析 USB Root Hub 分析 USB Hub 检测设备 文章目录 系列文章目录一、引言二、hub_eventshub_port_connect_changeusb_alloc_devusb_set_device_statehub_port_initusb_new_device 一、引言 USB Hub 检测设备 一文中讲到,当有 USB 插入时&…...

安卓开发使用Gemini高效AI开发-Android Studio 中使用Gemini
Gemini 是Android Studio最新版本中内嵌的AI工具,它可以通过代码补全、解释代码、提供改进建议、错误分析等方式帮助开发者提高编码效率。当然,与目前大多数AI工具一样,Gemini有时可能会"非常自信"地提供不准确、错误的信息&#x…...

wangEditor富文本插件在vue项目中使用和媒体上传的实现
wangEditor是前端一个比较流行的简洁易用,功能强大的前端富文本编辑器,支持 JS Vue React,提供了很多丰富的功能,下面手把手教你实现wangWditor富文本插件在vue项目中配置,保存、图片上传等功能。无脑ctrlc即可 基本功…...

ESP-IDF学习记录(2)ESP-IDF 扩展的简单使用
傻瓜式记录一个示例的打开,编译,运行。后面我再一个个运行简单分析每个demo的内容。 1.打开示例代码 2.选择项目,文件夹 3.选择串口 4.选择调试方式 5.根据硬件GPIO口配置menuconfig 6.构建项目 7.烧录设备,选择串口UART方式 运行…...
python中函数的用法总结(二阶段)
话接上回,继续讲下函数的用法 10. 函数的注解(Function Annotations) Python 3 引入了函数注解,允许你在函数定义时给参数和返回值添加注解。注解并不影响函数的实际行为,它们更多地用于代码的可读性、文档生成以及静…...
一份关于 Ubuntu 系统下代理配置的故障排查笔记
Ubuntu下代理配置故障排查指南 问题描述 在 Ubuntu 系统中开启了代理模式但访问依然很慢或无法访问。 排查步骤 1. 检查代理服务状态 # 检查端口监听状态 sudo apt install net-tools # 如果未安装 netstat sudo netstat -tulpn | grep 7897 # network statistics# 正…...
使用 Colyseus 构建多人实时白板应用
使用 Colyseus 构建多人实时白板应用 使用 Colyseus 构建多人实时白板应用涉及以下几个关键步骤:设置服务器、设计房间逻辑、同步客户端状态以及实现前端交互。以下是详细的实现流程: 0. 示例白板功能 基础功能 实时绘制同步: 用户在白板上绘制时,其绘制的点会立即显示在…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...