Jetpack составляет список mutableStateOf, который не вызывает повторную композицию при изменении значения свойства в классе элементов списка.

Я думаю, что здесь мне не хватает основной концепции Jetpack Compose. У меня возникла проблема, когда я пытаюсь изменитьnon-constructor data class propertyвнутри компонуемого, когда этот компонуемый является частью наблюдаемого списка.

Не работает: (sadPropertyне объявлен в конструкторе)

      data class IntWrapper(val actualInt: Int = 0) {
var sadProperty: Int = 0
}

@Preview
@Composable
fun test() {
var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))}

    fun onClick(item: IntWrapper) {
        val indexOf = state.indexOf(item)
        val newState = state.minus(item).toMutableList()
        val copy = item.copy()
        copy.sadProperty = Random.nextInt()
        newState.add(indexOf, copy)
        state = newState
    }
    
    Column() {
        for (item in state) {
            Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)})
        }
    }

}

Работает: (actualIntобъявляется в конструкторе)

      data class IntWrapper(var actualInt: Int = 0) {
var sadProperty: Int = 0
}

@Preview
@Composable
fun test() {
var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))}

    fun onClick(item: IntWrapper) {
        val indexOf = state.indexOf(item)
        val newState = state.minus(item).toMutableList()
        val copy = item.copy()
        copy.actualInt = Random.nextInt()
        newState.add(indexOf, copy)
        state = newState
    }
    
    Column() {
        for (item in state) {
            Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)})
        }
    }

}

Может кто-нибудь объяснить, почему это происходит?

2 ответа

Это похоже на вопрос и о том, и оKotlinкласс данных, голый со мной, я постараюсь изо всех сил.

Давайте начнем с классов данных Kotlin.

Согласно оData Class

Компилятор автоматически получает следующие элементы из всех свойств, объявленных в основном конструкторе:

  • пара equals()/hashCode()
  • toString() вида "Пользователь(имя=Джон, возраст=42)"
  • componentN() функции, соответствующие свойствам в порядке их объявления.
  • копировать ().

В вашем классе данных есть один Primary Constructor, круглая скобка, следующая за именем класса, и одно свойство, объявленное внутри него.

      data class IntWrapper(val actualInt: Int = 0) {
      var sadProperty: Int = 0
}

с этим, мы можем сказать, что ваш класс данных имеет

  • 1 компонент ()
  • toString() формыIntWrapper(actualInt=?)
  • сгенерированныйcopy()функция
  • сгенерированная пара equals()/hashCode()

и снова основано на документам Котлинадокументах:

Компилятор использует только свойства, определенные внутри основного конструктора, для автоматически созданных функций. Чтобы исключить свойство из сгенерированных реализаций, объявите его внутри тела класса:

The equalsбудет использовать/оценивать только свойство, объявленное изIntWrapper'sпервичный конструктор (т.е.actualInt : Int) и исключается из него, потому что находится в части тела класса данных.

Теперь рассмотрим следующее:

      val intWrapper1 = IntWrapper(actualInt = 5)
intWrapper1.sadProperty = 5

val intWrapper2 = IntWrapper(actualInt = 5)
intWrapper2.sadProperty = 10

Log.e("AreTheyEqual?", "${intWrapper1 == intWrapper2}")

он печатает,

      E/AreTheyEqual?: true

посколькуequalityвидит, что оба производных свойства имеют одинаковое значение5, исключается из этого сравнения.

      val intWrapper1 = IntWrapper(actualInt = 5)
intWrapper1.sadProperty = 5

val intWrapper2 = IntWrapper(actualInt = 10)
intWrapper2.sadProperty = 5

отпечатки,

      E/AreTheyEqual?: false

потому что сгенерированный equals проверяет, что сгенерированный компонент (actualInt) НЕ совпадает с двумя экземплярами.

Теперь собираюсьJetpack Compose, применяя все, что мы понимаем с классами данных,

  • Первая соответствует всем требованиямdata class, он создает новый объект с новым значением, и это то, что нужно для запуска.

  • Второйtestне сработаетre-composition,Composeвсе еще видит то же самоеIntWrapperнапример, потому чтоsadPropertyне является частью сгенерированных компонентов, которые будут использоваться операцией равенства класса данных.

В Compose для успешного выполнения операции рекомпозиции необходимо использовать один из следующих двух методов:

1 - с помощью mutableStateListOf(), однако при обновлении значения элемента в списке выполняется операция перекомпоновки

2- Используя свой собственный метод, который вы опубликовали

Но для второго метода вам нужно сообщить Compose, что factInt изменился, поэтому вам нужно создать новый экземпляр int.

Если вы не хотите этого делать, вам нужно подробнее объяснить свой сценарий, чтобы я мог предоставить более полное руководство.

Другие вопросы по тегам