Desarrollo de aplicaciones HarmonyOS / OpenHarmony: control de representación del lenguaje ArkTS para cada representación en bucle

ForEach realiza una representación en bucle basada en datos de tipo matriz. Nota: A partir de la versión 9 de API, esta interfaz admite el uso en tarjetas ArkTS.
1. Descripción de la interfaz

ForEach(
  arr: any[], 
  itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number) => string 
)

#2023Blind Box+Code#HarmonyOS/OpenHarmony desarrollo de aplicaciones-control de representación del lenguaje ArkTS para cada representación en bucle-comunidad de software básico de código abierto


2. Restricciones de uso
ForEach debe usarse dentro del componente contenedor.
Los componentes secundarios generados deben ser componentes secundarios que puedan incluirse en el componente contenedor principal de ForEach.
Permite incluir la representación condicional if/else en las funciones del generador de subcomponentes y también permite que ForEach se incluya en las declaraciones de representación condicional if/else.
El orden de llamada de la función itemGenerator no es necesariamente el mismo que el de los elementos de datos en la matriz. Durante el proceso de desarrollo, no asuma si las funciones itemGenerator y keyGenerator se ejecutan ni su orden de ejecución. Por ejemplo, es posible que el siguiente ejemplo no se ejecute correctamente

ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }), 
  item => Text(`${item.i}. item.data.label`),
  item => item.data.id.toString())

3. Sugerencias de los desarrolladores
Se recomienda que los desarrolladores no asuman el orden de ejecución de los constructores de elementos. El orden de ejecución puede no ser el orden en que se ordenan los elementos de la matriz.
No haga suposiciones sobre si un elemento de la matriz se representa inicialmente. La representación inicial de ForEach crea todos los elementos de la matriz cuando se representa @Component por primera vez. Este comportamiento se puede cambiar al modo de carga diferida en versiones posteriores del marco.
El uso del parámetro index tiene un impacto negativo grave en el rendimiento de la actualización de la interfaz de usuario, así que trate de evitarlo.
Si el parámetro de índice se usa en el constructor del elemento, el parámetro también debe usarse en la función de índice del elemento. De lo contrario, si la función de índice del elemento no utiliza el parámetro de índice, el marco también tendrá en cuenta el índice cuando ForEach genere el valor clave real, y el índice se empalmará al final de forma predeterminada.
4. Escenarios de uso
1. Ejemplo simple de ForEach
Cree tres componentes de texto y división basados ​​en datos arr.

@Entry
@Component
struct MyComponent {
  @State arr: number[] = [10, 20, 30];

  build() {
    Column({ space: 5 }) {
      Button('Reverse Array')
        .onClick(() => {
          this.arr.reverse();
        })
      ForEach(this.arr, (item: number) => {
        Text(`item value: ${item}`).fontSize(18)
        Divider().strokeWidth(2)
      }, (item: number) => item.toString())
    }
  }
}

2. Ejemplo complejo para cada uno

@Component
struct CounterView {
  label: string;
  @State count: number = 0;

  build() {
    Button(`${this.label}-${this.count} click +1`)
      .width(300).height(40)
      .backgroundColor('#a0ffa0')
      .onClick(() => {
        this.count++;
      })
  }
}

@Entry
@Component
struct MainView {
  @State arr: number[] = Array.from(Array(10).keys()); // [0.,.9]
  nextUnused: number = this.arr.length;

  build() {
    Column() {
      Button(`push new item`)
        .onClick(() => {
          this.arr.push(this.nextUnused++)
        })
        .width(300).height(40)
      Button(`pop last item`)
        .onClick(() => {
          this.arr.pop()
        })
        .width(300).height(40)
      Button(`prepend new item (unshift)`)
        .onClick(() => {
          this.arr.unshift(this.nextUnused++)
        })
        .width(300).height(40)
      Button(`remove first item (shift)`)
        .onClick(() => {
          this.arr.shift()
        })
        .width(300).height(40)
      Button(`insert at pos ${Math.floor(this.arr.length / 2)}`)
        .onClick(() => {
          this.arr.splice(Math.floor(this.arr.length / 2), 0, this.nextUnused++);
        })
        .width(300).height(40)
      Button(`remove at pos ${Math.floor(this.arr.length / 2)}`)
        .onClick(() => {
          this.arr.splice(Math.floor(this.arr.length / 2), 1);
        })
        .width(300).height(40)
      Button(`set at pos ${Math.floor(this.arr.length / 2)} to ${this.nextUnused}`)
        .onClick(() => {
          this.arr[Math.floor(this.arr.length / 2)] = this.nextUnused++;
        })
        .width(300).height(40)
      ForEach(this.arr,
        (item) => {
          CounterView({ label: item.toString() })
        },
        (item) => item.toString()
      )
    }
  }

MainView contiene una serie de números decorados con @State. Agregar, eliminar y reemplazar elementos de la matriz son eventos de cambio observables. Cuando ocurren estos eventos, se actualizará ForEach dentro de MainView.
La función de índice de elementos crea un valor clave único y persistente para cada elemento de la matriz. El marco ArkUI utiliza este valor clave para determinar si el elemento en la matriz ha cambiado. Siempre que el valor clave sea el mismo, el valor del elemento de la matriz Se supone que no cambia, pero su posición en el índice puede cambiar. La premisa de este mecanismo es que diferentes elementos de la matriz no pueden tener el mismo valor clave.
Usando la ID calculada, el marco puede distinguir entre elementos de matriz agregados, eliminados y retenidos:
(1) El marco eliminará el componente de interfaz de usuario del elemento de matriz eliminado.
(2) El marco solo ejecuta el constructor de elementos para los elementos de matriz recién agregados.
(3) El marco no ejecuta constructores de elementos para los elementos de la matriz retenidos. Si el índice del elemento en la matriz ha cambiado, el marco solo moverá su componente de interfaz de usuario de acuerdo con el nuevo orden, pero no actualizará el componente de interfaz de usuario.
Se recomienda utilizar la función de índice de elementos, pero esto es opcional. Los ID generados deben ser únicos, lo que significa que no se puede calcular el mismo ID para diferentes elementos de la matriz. Incluso si dos elementos de la matriz tienen el mismo valor, sus ID deben ser diferentes.
Si el valor del elemento de la matriz cambia, la ID debe cambiar.
Ejemplo: como se mencionó anteriormente, la función de generación de identificación es opcional. Aquí está ForEach sin la función de índice de elementos:
ForEach(this.arr,
(item) => { CounterView({ label: item.toString() }) } )



Si no se proporciona ninguna función de ID de elemento, el marco intenta detectar de forma inteligente los cambios en la matriz al actualizar ForEach. Sin embargo, puede eliminar componentes secundarios y volver a ejecutar el constructor de elementos para los elementos de la matriz que se mueven en la matriz (se cambia el índice). En el ejemplo anterior, esto cambiaría el comportamiento de la aplicación con respecto al estado del contador CounterView. Cuando se crea una nueva instancia de CounterView, el valor del contador se inicializará en 0.
3. Para cada ejemplo usando @ObjectLink
Cuando es necesario retener el estado de los subcomponentes repetidos, @ObjectLink puede enviar el estado al componente principal en el árbol de componentes.

let NextID: number = 0;

@Observed
class MyCounter {
  public id: number;
  public c: number;

  constructor(c: number) {
    this.id = NextID++;
    this.c = c;
  }
}

@Component
struct CounterView {
  @ObjectLink counter: MyCounter;
  label: string = 'CounterView';

  build() {
    Button(`CounterView [${this.label}] this.counter.c=${this.counter.c} +1`)
      .width(200).height(50)
      .onClick(() => {
        this.counter.c += 1;
      })
  }
}

@Entry
@Component
struct MainView {
  @State firstIndex: number = 0;
  @State counters: Array<MyCounter> = [new MyCounter(0), new MyCounter(0), new MyCounter(0),
    new MyCounter(0), new MyCounter(0)];

  build() {
    Column() {
      ForEach(this.counters.slice(this.firstIndex, this.firstIndex + 3),
        (item) => {
          CounterView({ label: `Counter item #${item.id}`, counter: item })
        },
        (item) => item.id.toString()
      )
      Button(`Counters: shift up`)
        .width(200).height(50)
        .onClick(() => {
          this.firstIndex = Math.min(this.firstIndex + 1, this.counters.length - 3);
        })
      Button(`counters: shift down`)
        .width(200).height(50)
        .onClick(() => {
          this.firstIndex = Math.max(0, this.firstIndex - 1);
        })
    }
  }
}

Cuando se incrementa el valor de firstIndex, ForEach dentro de Mainview se actualizará y eliminará el subcomponente CounterView asociado con el ID del elemento firstIndex-1. Para el elemento de matriz con ID firstindex + 3, se creará una nueva instancia de subcomponente CounterView. Dado que el valor del contador de la variable de estado del subcomponente CounterView lo mantiene el componente principal Mainview, la reconstrucción de la instancia del subcomponente CounterView no reconstruirá el valor del contador de la variable de estado.
Tenga en cuenta que violar las reglas de identificación de elementos de matriz anteriores es el error de desarrollo de aplicaciones más común, especialmente en escenarios de matriz, porque es fácil agregar números repetidos durante la ejecución.
4. El uso anidado de ForEach
permite anidar ForEach en otro ForEach en el mismo componente, pero es más recomendable dividir el componente en dos y que cada constructor contenga solo un ForEach. El siguiente es un contraejemplo del anidamiento ForEach.

class Month {
  year: number;
  month: number;
  days: number[];

  constructor(year: number, month: number, days: number[]) {
    this.year = year;
    this.month = month;
    this.days = days;
  }
}
@Component
struct CalendarExample {
  // 模拟6个月
  @State calendar : Month[] = [
    new Month(2020, 1, [...Array(31).keys()]),
    new Month(2020, 2, [...Array(28).keys()]),
    new Month(2020, 3, [...Array(31).keys()]),
    new Month(2020, 4, [...Array(30).keys()]),
    new Month(2020, 5, [...Array(31).keys()]),
    new Month(2020, 6, [...Array(30).keys()])
  ]
  build() {
    Column() {
      Button() {
        Text('next month')
      }.onClick(() => {
        this.calendar.shift()
        this.calendar.push(new Month(year: 2020, month: 7, days: [...Array(31).keys()]))
      })
      ForEach(this.calendar,
        (item: Month) => {
          ForEach(item.days,
            (day : number) => {
              // 构建日期块
            },
            (day : number) => day.toString()
          )// 内部ForEach
        },
        (item: Month) => (item.year * 12 + item.month).toString() // 字段与年和月一起使用,作为月份的唯一ID。
      )// 外部ForEach
    }
  }
}


Hay dos problemas con el ejemplo anterior:
(1) El código no se puede leer correctamente.
(2) Para la forma de estructura de matriz anterior de datos de año y mes, dado que el marco no puede observar cambios en la estructura de datos del mes en la matriz (como cambios en la matriz de días), el ForEach interno no puede actualizar la visualización de la fecha.
Se recomienda dividir el Calendario en subcomponentes Año, Mes y Día al diseñar la aplicación. Defina una clase modelo "Día" para contener información sobre el día y decore esta clase con @Observed. El componente DayView utiliza ObjectLink para decorar variables para vincular datos del día. Haga lo mismo para las clases de modelo MonthView y Month.
5. Ejemplos de uso del parámetro de índice opcional en ForEach
Puede utilizar el parámetro de índice opcional en el constructor y en la función de generación de ID.

@Entry
@Component
struct ForEachWithIndex {
  @State arr: number[] = [4, 3, 1, 5];

  build() {
    Column() {
      ForEach(this.arr,
        (it, indx) => {
          Text(`Item: ${indx} - ${it}`)
        },
        (it, indx) => {
          return `${indx} - ${it}`
        }
      )
    }
  }
}

La función de generación de ID debe construirse correctamente. Cuando se usa el parámetro de índice en el constructor de elementos, la función de generación de ID también debe usar el parámetro de índice para generar una ID única y la ID del elemento de matriz de origen determinado. Cuando cambia la posición del índice de un elemento de la matriz en la matriz, su ID cambia.
Este ejemplo también ilustra que el parámetro de índice puede causar una degradación significativa del rendimiento. Incluso si un elemento se mueve dentro de la matriz de origen sin modificaciones, la interfaz de usuario que depende de ese elemento de la matriz aún debe volver a representarse porque el índice cambia. Por ejemplo, cuando se utiliza la clasificación por índice, la matriz solo necesita mover los nodos secundarios de la interfaz de usuario no modificados de ForEach a la ubicación correcta, lo cual es una operación liviana para el marco. Cuando se utilizan índices, es necesario reconstruir todos los nodos secundarios de la interfaz de usuario, lo cual es una operación mucho más pesada.

 

Supongo que te gusta

Origin blog.csdn.net/weixin_69135651/article/details/132358565
Recomendado
Clasificación