HarmonyOS鸿蒙Next中如何动态创建控件,并动态修改aspectRatio属性?

HarmonyOS鸿蒙Next中如何动态创建控件,并动态修改aspectRatio属性? 我写了一个demo,我想动态创建控件,并动态修改aspectRatio属性,目前控件是可以动态创建,但aspectRatio属性动态修改不生效,不知道什么原因。

demo下载地址:https://gitee.com/chen_yi_ze/harmony-test-ratio

11 回复
  • 方案一,V1版本,需要替换整个数组的元素:
    [@Observed](/user/Observed)
    class ItemData {
            public ratio: number = 0;
    }
    
    [@Entry](/user/Entry)
    [@Component](/user/Component)
    struct Index25 {
            @State items: ItemData[] = [];
    
            build() {
                    Column({ space: 20 }) {
                            Button('新建控件')
                                    .onClick(() => {
                                            this.items.push({ ratio: 16 / 9 });
                                    })
    
                            // 动态渲染每个控件
                            ForEach(this.items, (item: ItemData, index: number) => {
                                    // 控件的宽高比绑定到 item.ratio
                                    Row() {
                                            Text(`${item.ratio}`)
                                    }
                                    .width('100%')
                                    .aspectRatio(item.ratio)
                                    .backgroundColor('#FF6B6B')
                                    .animation({ duration: 300 }) // 平滑过渡
    
                                    // 调节当前控件比例
                                    Row({ space: 10 }) {
                                            Button('1:1').onClick(() => {
                                                    // item.ratio = 1;
                                                    let o = new ItemData();
                                                    o.ratio = 1;
                                                    this.items[index] = o;
                                            })
                                            Button('4:3').onClick(() => {
                                                    // item.ratio = 4 / 3;
                                                    let o = new ItemData();
                                                    o.ratio = 4 / 3;
                                                    this.items[index] = o;
                                            })
                                            Button('16:9').onClick(() => {
                                                    // item.ratio = 16 / 9;
                                                    let o = new ItemData();
                                                    o.ratio = 16 / 9;
                                                    this.items[index] = o;
                                            })
                                    }
                            })
                    }.width('100%')
                    .padding(20)
            }
    }
    
  • 方案二,V2版本:
    [@ObservedV2](/user/ObservedV2)
    class ItemData {
            @Trace
            public ratio: number = 0;
    }
    
    [@Entry](/user/Entry)
    [@ComponentV2](/user/ComponentV2)
    struct Index25 {
            @Local items: ItemData[] = [];
    
            build() {
                    Column({ space: 20 }) {
                            Button('新建控件')
                                    .onClick(() => {
                                            let o = new ItemData(); //必须使用new
                                            o.ratio = 16 / 9;
                                            this.items.push(o);
                                    })
    
                            // 动态渲染每个控件
                            ForEach(this.items, (item: ItemData, index: number) => {
                                    // 控件的宽高比绑定到 item.ratio
                                    Row() {
                                            Text(`${item.ratio}`)
                                    }
                                    .width('100%')
                                    .aspectRatio(item.ratio)
                                    .backgroundColor('#FF6B6B')
                                    .animation({ duration: 300 }) // 平滑过渡
    
                                    // 调节当前控件比例
                                    Row({ space: 10 }) {
                                            Button('1:1').onClick(() => {
                                                    item.ratio = 1;
                                            })
                                            Button('4:3').onClick(() => {
                                                    item.ratio = 4 / 3;
                                            })
                                            Button('16:9').onClick(() => {
                                                    item.ratio = 16 / 9;
                                            })
                                    }
                            })
                    }.width('100%')
                    .padding(20)
            }
    }
    

更多关于HarmonyOS鸿蒙Next中如何动态创建控件,并动态修改aspectRatio属性?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


方案二中,这个为什么“let o = new ItemData(); //必须使用new”要必须使用new?

使用@ObservedV2@Trace装饰器的类,需通过new操作符实例化后,才具备被观测变化的能力。

[@ObservedV2装饰器和@Trace装饰器:类属性变化观测-管理数据对象的状态-状态管理(V2)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-observedv2-and-trace#observedv2装饰对象的序列化与反序列化)

我大概理解,我觉得可能是用{}创建的对象它不属于ItemData类,所以系统就不去观察了,非常感谢你的详细回答。

这是新手经常碰到的问题:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state

@State装饰器 观察变化

  • 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
  • 当装饰的数据类型为class或Object时,可以观察到自身的赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性。
  • 当装饰的对象是Array时,可以观察到Array整体的赋值及数组元素的赋值,同时可以通过调用Array的接口push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort更新Array中的数据。数组项中嵌套的属性赋值无法观察。详见装饰Array类型变量。
  • 当装饰的对象是Date时,可以观察到Date的赋值,以及通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds更新Date的属性,详见装饰Date类型变量。
  • 当装饰的变量是Map时,可以观察到Map整体的赋值,以及通过调用Map的接口set, clear, delete更新Map的值。详见装饰Map类型变量。
  • 当装饰的变量是Set时,可以观察到Set整体的赋值,以及通过调用Set的接口add, clear, delete更新Set的值。详见装饰Set类型变量。
  • 对嵌套对象的属性直接赋值无法被框架观察到,因此不会触发UI刷新。

学习了

@Observed
class ItemData
{
  public ratio: number = 0;
}

@Entry
@Component
struct Index
{
  @State items: ItemData[] = [];

  build()
  {
    Column( { space: 20 } )
    {
      Button( '新建控件' )
        .onClick( () => this.items.push( { ratio: 16 / 9 } ) )

      // 动态渲染每个控件
     //Todo :ForEach( this.items, ( item: ItemData ) =>
    ForEach( this.items, ( item: ItemData, index: number ) =>
      {
        // 控件的宽高比绑定到 item.ratio
        Row( )
          .width( '100%' )
          .aspectRatio( item.ratio )
          .backgroundColor( '#FF6B6B' )
          .animation( { duration: 300 } ) // 平滑过渡

        // 调节当前控件比例
        Row( { space: 10 } )
        {
          Button( '1:1' ).onClick( () =>
          {
            //Todo : item.ratio = 1;
            const res = new ItemData()
            res.ratio = 1;
            this.items[index] = res;
          } )
          Button( '4:3' ).onClick( () =>
          {
            //Todo : item.ratio = 4 / 3;
            const res = new ItemData()
            res.ratio = 4 / 3;
            this.items[index] = res;
          } )
          Button( '16:9' ).onClick( () =>
          {
            //Todo : item.ratio = 16 / 9;
            const res = new ItemData()
            res.ratio = 16 / 9;
            this.items[index] = res;
          })
        }
      })
    }.width( '100%' )
    .padding( 20 )
  }
}

改变整个对象即可

你这个方法也行,就是浪费一点内存,非常感谢。

在HarmonyOS Next中,使用ArkTS声明式UI,通过@State定义aspectRatio变量,在build()中创建组件并绑定该属性(如.aspectRatio(this.ratio))。动态修改时,在事件回调中更新this.ratio的值,系统会自动触发UI刷新。例如:Button('修改比例').onClick(() => { this.ratio = 1.5 })

可能是 aspectRatio 属性仅用于组件初次布局,ArkUI 框架不支持在运行时动态修改它。动态赋新值不会触发重新测量和布局。
若需动态调整宽高比,建议改用状态变量(如 @State)控制宽/高,自行按比例计算数值并绑定到组件上,例如:

@State aspect: number = 2
@State width: number = 100

build() {
  Row()
    .width(this.width)
    .height(this.width / this.aspect)
}

这样在修改 aspectwidth 时比例会实时生效。如需完全更换比例,也可以结合 if/else 条件渲染,触发组件重建并重新设定 aspectRatio

回到顶部