kotlin 如何在状态被重新分配为当前值时触发JetPack重组

6l7fqoea  于 2023-02-16  发布在  Kotlin
关注(0)|答案(1)|浏览(134)

下面的代码可以正常工作:每当用户单击canvas本身或单击topBar图标时,canvas都将重新组合,无论单击次数或顺序如何。此外,state变量值揭示了我想知道的一些信息:用户单击的位置。(值0和1表示单击了图标,值2和3表示画布)。
但是,如果canvasState和iconState变量被设置为各自的V1函数而不是V2函数,则不会检测到连续多次单击画布或图标,这显然是因为V1函数可以重新将相同的值赋给状态变量,这与V2函数不同。
因为我使用的是neverEqualPolicy(),所以我想我不必给状态变量赋一个不同的值来触发重组。作为Kotlin和Compose的新手,我误解了什么?

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApp()
        }
    }
}

@Composable
fun MyApp() {
    var state by remember { mutableStateOf(value = 0, policy = neverEqualPolicy()) }
    val canvasStateV1 = { state = 0 }
    val iconStateV1 = { state = 2 }
    val canvasStateV2 = { state = if (state == 0) { 1 } else { 0 } }
    val iconStateV2 = { state = if (state == 2) { 3 } else { 2 } }
    val iconState = iconStateV2
    val canvasState = canvasStateV2
    Scaffold(
        topBar = { TopBar(canvasState) },
        content = { padding ->
            Column(Modifier.padding(padding)) {
                Screen(state, iconState)
            }
        }
    )
}

@Composable
fun TopBar(iconState: () -> Unit) {
    TopAppBar(
        title = { Text("This is a test") },
        actions = {
            IconButton(onClick = { iconState() }) {
                Icon(Icons.Filled.AddCircle, null)
            }
        }
    )
}

@Composable
fun Screen(state: Int, canvasState: () -> Unit) {

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Box(
            modifier = Modifier
                .aspectRatio(ratio = 1f)
                .background(color = MaterialTheme.colors.onSurface)
                .pointerInput(Unit) {
                    detectTapGestures(
                        onTap = { canvasState() },
                    )
                }
        ) {
            Canvas(
                modifier = Modifier.fillMaxSize().clipToBounds()
            ) {
                Log.d("Debug", "Canvas: state = $state")
            }
        }
    }
}

我不知道还有什么可以让neverEqualPolicy()按预期工作。

lrl1mhuk

lrl1mhuk1#

我认为这主要是因为函数Screen()是可跳过的。如果您将状态添加为MutableState而不是Int本身,您将看到每次状态值更新时都会调用Log.d。将Screen()函数合并到MyApp中的Column也是如此
Compose在构建时分析每个函数。screen函数接收一个整数值,这是一个不可变的值,所以函数本身是可以跳过的。
要分析哪些函数是可跳过/稳定的(哪些不是),您可以在构建阶段运行一个报告。

**EDIT:**在本例中,您有两个按钮,一个更改值,一个设置相同的值。当设置相同的值时,您只能看到本地重组的Log.d。当更改状态值时,您可以看到两个日志行。本地和外部都经过重组。

@Composable
fun StackOverflowApp() {
    var state by remember { mutableStateOf(value = 0, policy = neverEqualPolicy()) }

Column() {
    Button(onClick = { state = state }) {
        Text(text = "State same value")
    }
    Button(onClick = { state += 1 }) {
        Text(text = "State up")
    }

    Text(text = "[local] current State = $state")
    Log.d("TAG","Recomposition local")
    ExternalText(state)
}
}

/**
 * A skippable function
 * 
 * restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ExternalText(
    stable state: Int
)
 */
@Composable
fun ExternalText(state: Int){
    Text(text = "[external] current State = $state")
    Log.d("TAG","Recomposition external")
}

您也可以传递mutableState而不是int值本身,当您传递mutableState时,neverEqualPolicy仍然有效。

@Composable
fun StackOverflowApp() {
    var state = remember { mutableStateOf(value = 0, policy = neverEqualPolicy()) }

    Column() {
        Button(onClick = { state.value = state.value }) {
            Text(text = "State same value")
        }
        Button(onClick = { state.value += 1 }) {
            Text(text = "State up")
        }

        Text(text = "[local] current State = ${state.value}")
        Log.d("TAG","Recomposition internal")
        ExternalText(state)
    }
}

@Composable
fun ExternalText(state: MutableState<Int>){
    Text(text = "[external] current State = ${state.value}")
    Log.d("TAG","Recomposition external")
}

相关问题