Как наблюдать эффект staticCompositionLocalOf()?Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Как наблюдать эффект staticCompositionLocalOf()?

Сообщение Anonymous »

Я хотел бы наблюдать, как использование staticCompositionLocalOf вызывает «перекомпоновку всего лямбда-контента CompositionLocalProvider» согласно документации androids.compose.runtime:

В отличие от CompositionLocalOf, чтение staticCompositionLocalOf не отслеживается композитором и не меняет значение, указанное в CompositionLocalProvider > вызов приведет к перекомпоновке всего содержимого, а не только тех мест, где в композиции используется локальное значение.

Запуск прикрепленного кода , я ожидал, что компонуемый текст как в setContent, так и в компонуемом Node, а также сам компонуемый Node будет перекомпонован, когда я нажму кнопку. Однако инспектор слоев Android Studio показал, что были перекомпонованы только Leaf и Text, которые можно компоновать в Leaf. Как видно на прилагаемых снимках экрана, изменение staticCompositionLocalOf во второй строке кода на CompositionLocalOf привело к тем же рекомпозициям. Я также попробовал counter = mutableStateOf(Counter()) с соответствующим образом обновленным типом LocalCounter, без каких-либо различий в результатах.
Изображение
Изображение

Код: Выделить всё

data class Counter (var count: MutableState = mutableStateOf(0))
val LocalCounter = staticCompositionLocalOf {
error("CompositionLocal LocalCounter not provided")
}

class MainActivity : ComponentActivity() {
private var counter = Counter()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
CompositionLocalProvider(LocalCounter provides counter) {
Column {
Text("setContent ${LocalCounter}")
Node()
Button(content = { Text("+1") },
onClick = { counter.count.value += 1 }
)
}
}
}
}
}

@Composable
fun Node() {
Text("Node")
Leaf()
}

@Composable
fun Leaf() {
val counter = LocalCounter.current
Text("Leaf count: ${counter.count.value}")
}
Я запускал код с помощью Android Studio Jellyfish Patch 1 с эмулятором Pixel 8 API 34, Android Studio Koala Canary 2 с эмулятором Pixel 8 API VanillaIceCream и физического Pixel 4a с API 33. Все с Kotlin 1.9.24 и с теми же рекомпозициями.
Связанные вопросы о stackoverflow и Интернете в целом чаще всего задаются об использовании CompositionLocal, например, как и когда именно CompositionLocal устанавливает значения неявно? или https://stackoverflow.com/a/77496284, или о концептуальной разнице между staticCompositionLocalOf и CompositionLocalOf, которую объясняет официальная документация. Мне не удалось найти ни одного примера кода, показывающего, как можно показать разницу между этими двумя.
Ни одного примера, показывающего рекомпозицию составных элементов, которые не поддерживаются. используйте предоставленное значение, но исключительно из-за использования staticCompositionLocalOf, мы будем очень признательны!
Обновленный код на основе ответа Яна Итора:< /strong>
В одной из моих первоначальных неудачных попыток вместо наблюдения за предоставленным значением типа Counter я пытался просмотреть предоставленное значение типа MutableState и обновление счетчика в изменяемом значении при нажатии кнопки. Вот различия с неизменяемым свойством в классе данных:

Код: Выделить всё

# line 1:
data class Counter(var count: Int = 0)
# line 2:
val LocalCounter = staticCompositionLocalOf {
# line 6:
private var counter = mutableStateOf(Counter())
# line 18:
onClick = { counter.value = Counter(counter.value.count + 1) }
# line 35:
Text("Leaf count: ${counter.value.count}")
Полученные рекомпозиции были такими же, как исходный код. Насколько я понимаю, хотя counter.value был обновлен, сам counter этого не сделал - значение CompositionLocal по-прежнему содержит тот же указатель на тот же блок, экземпляр Counter в поле изменился, но поле и указатель на него остались прежними. Таким образом, CompositionLocal не обнаружил никаких изменений.
Чтобы увидеть эффект staticCompositionLocalOf, необходимо изменить значение внутри самого поля CompositionLocal. Вот обновленный код:

Код: Выделить всё

data class Counter(var count: Int = 0)
val LocalCounter = staticCompositionLocalOf {
error("CompositionLocal LocalCounter not provided")
}

class MainActivity : ComponentActivity() {
private var counter = mutableStateOf(Counter())

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
CompositionLocalProvider(LocalCounter provides counter.value) {
Column {
Text("setContent ${LocalCounter}")
Node()
Button(content = { Text("+1") },
onClick = { counter.value = Counter(counter.value.count + 1) }
)
}
}
}
}
}

@Composable
fun Node() {
Text("Node")
Leaf()
}

@Composable
fun Leaf() {
val counter = LocalCounter.current
Text("Leaf count: ${counter.count}")
}
(Использование делегирования свойств с помощью by в строке 6 избавляет от необходимости ссылаться на .value в строках 13 и 18 для более четкого синтаксиса, но я хотел четко указывайте используемые типы.)
Изображение

Чтобы уточнить официальную документацию, я хочу сказать, что если наблюдаемое изменяемое состояние является членом класса, но экземпляр класса, предоставленный CompositionLocal, сам по себе не является измениться, использование staticCompositionLocalOf предотвращает отслеживание предоставленного (неизмененного) экземпляра. С другой стороны, если сам предоставленный экземпляр изменяется, использование композицииLocalOf предотвращает повторную композицию компонуемых объектов, не учитывающих предоставленный экземпляр. Примеры первого: исходный Counter выше или коллекции, в которых отдельные элементы являются изменяемыми и наблюдаемыми, но сами экземпляры коллекций неизменны. Примеры последнего: замена целых экземпляров базовых типов, String, коллекций или пользовательских классов.

Подробнее здесь: https://stackoverflow.com/questions/785 ... ionlocalof
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»