有这段代码作为重现的最小示例
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Button
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.krokosha.focusrequester2.ui.theme.Test_delete_itTheme
private const val FIRST_SCREEN_ROUTE = "first_screen"
private const val SECOND_SCREEN_ROUTE = "second_screen"
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Test_delete_itTheme {
Surface(
modifier = Modifier.fillMaxSize(),
shape = RectangleShape
) {
Greeting()
}
}
}
}
}
@Composable
fun Greeting() {
val navigator: NavHostController = rememberNavController()
NavHost(
navController = navigator,
startDestination = FIRST_SCREEN_ROUTE
) {
composable(FIRST_SCREEN_ROUTE) {
Log.e("HERE", "1 SCREEN")
FirstScreen(onClick = { navigator.navigate(SECOND_SCREEN_ROUTE) }) }
composable(SECOND_SCREEN_ROUTE) {
Log.e("HERE", "2 SCREEN")
SecondScreen() }
}
}
@Composable
fun SecondScreen() {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Red.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center
) {
Text(text = "SECOND SCREEN")
}
}
@Composable
fun FirstScreen(onClick: () -> Unit) {
Row(modifier = Modifier
.fillMaxSize()
) {
LeftPanel()
RightPanel(onClick = onClick)
}
}
@Composable
fun RowScope.LeftPanel() {
val buttons: List<String> by rememberSaveable { mutableStateOf(List(5) { "Button ${it + 1}" }) }
LazyColumn(
modifier = Modifier
.background(Color.Blue.copy(alpha = 0.1f))
.fillMaxHeight()
.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
itemsIndexed(
items = buttons,
key = { idx, _ -> idx }
) { idx, _ ->
Button(
modifier = Modifier,
onClick = { /* nothing */ }
) {
Text(text = "Left Panel: $idx")
}
}
}
}
@Composable
fun RowScope.RightPanel(onClick: () -> Unit) {
val buttons: List<String> by rememberSaveable { mutableStateOf(List(4) { "Button ${it + 1}" }) }
Column(
modifier = Modifier
.background(Color.Green.copy(alpha = 0.1f))
.fillMaxHeight()
.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
LazyVerticalGrid(
modifier = Modifier.padding(16.dp),
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
itemsIndexed(
items = buttons,
key = { idx, _ -> idx }
) { idx, _ ->
Button(
modifier = Modifier
.padding(8.dp),
onClick = {
onClick()
}
) {
Text(text = "Right Panel: $idx")
}
}
}
}
}
我离开了登录状态composable(FIRST_SCREEN_ROUTE)
,composable(SECOND_SCREEN_ROUTE)
当我单击右侧面板(屏幕上)中的按钮时,我在日志中看到以下内容(以及 中的相同内容LayoutInspector
)
13:26:43.572 E 1 SCREEN
13:26:46.570 E 1 SCREEN
13:26:46.580 E 2 SCREEN
13:26:46.661 E 1 SCREEN
13:26:46.668 E 2 SCREEN
13:26:47.369 E 2 SCREEN
13:26:47.438 E 2 SCREEN
问题是——最空的例子加上最空的导航,切换屏幕时怎么会发生多次重组呢?
你有日志的地方有点错误。
当更改 UI 时,重组始终会转到最重要的父元素。
然后它开始向下遍历孩子并检查他们的参数。如果它们发生了变化,孩子们就会重新组合。
在您的情况下(移动到新屏幕)您有一个重新组合的方法
Greeting
,即它将被完全重新调用,这就是日志如此工作的原因。所以首先您需要在函数内传输日志
FirstScreen
并SecondScreen
现在我们看到这些函数被可疑地重绘了很多次。
问题就在这个地方:
FirstScreen(onClick = { navigator.navigate(SECOND_SCREEN_ROUTE) }) }
事实是你的点击代码不稳定。
要解决这个问题,你可以这样写:
现在点击很稳定,并且确实不会发生不必要的重组。