The*_*bal 5 android kotlin android-jetpack-compose jetpack-compose-navigation compose-recomposition
这是导致无限重组问题的代码
主要活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
val viewModel : MainViewModel by viewModel()
val state by viewModel.state.observeAsState()
NavHost(navController = navController, startDestination = "firstScreen") {
composable("firstScreen") { FirstScreen(
navigate = {
navController.navigate("secondScreen")
}, updateState = {
viewModel.getState()
},
state
)}
composable("secondScreen") { SecondScreen() }
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型
class MainViewModel : ViewModel() {
//var state = MutableStateFlow(0)
private val _state = MutableLiveData(0)
val state: LiveData<Int> = _state
fun getState() {
_state.value = 1
}
}
Run Code Online (Sandbox Code Playgroud)
第一屏
@Composable
fun FirstScreen(
navigate: () -> Unit,
updateState: () -> Unit,
state: Int?
) {
Log.e("state",state.toString())
Button(onClick = {
updateState()
}) {
Text(text = "aaaaaaaa")
}
if(state == 1) {
Log.e("navigate",state.toString())
navigate()
}
}
Run Code Online (Sandbox Code Playgroud)
第二屏
@Composable
fun SecondScreen() {...}
Run Code Online (Sandbox Code Playgroud)
按下按钮会更改视图模型中的状态,如果它更改为 1,则会触发导航到第二个屏幕,但第一个屏幕会无限重组并阻止整个过程
编辑
@Composable
fun FirstScreen(
navigate: () -> Unit,
updateState: () -> Unit,
state: Int?
) {
Log.e("state",state.toString())
Button(onClick = {
updateState()
}) {
Text(text = "aaaaaaaa")
}
LaunchedEffect(state) {
if (state == 1) {
Log.e("navigate", state.toString())
navigate()
}
}
}
Run Code Online (Sandbox Code Playgroud)
这解决了问题
这是因为您正在基于条件属性进行导航,该FirstScreen条件属性是可组合项的一部分,并且对该属性的更改超出了范围FirstScreen's,如果该条件属性的值没有更改,则每次更新时它都会NavHost在您的casestate保留1并将始终执行其块。
if(state == 1) {
...
navigate() // navigation
}
Run Code Online (Sandbox Code Playgroud)
您的经历可以通过以下事件来总结:
FirstScreen和SecondScreen(初始NavHost composition)FirstScreen观察一个state值为0state1单击按钮后变为FirstScreen re-composes,满足条件( ),执行当前时间state==1的导航1stre-composesFirstScreen's state剩余,1仍满足条件( ),暂时再次state==1执行导航2ndre-composesFirstScreen's state仍为1,满足条件( state==1),再次执行本次3rd导航根据官方文档,
您应该仅将 navigator() 作为回调的一部分调用,而不是作为可组合项本身的一部分,以避免在每次重组时调用 navigator()。
我建议将其视为navigation一次性事件,在内部进行LaunchedEffect并从发射中观察SharedFlow。以下是解决您问题的简短解决方法。
有一堂密封课UiEvent,
sealed class UiEvent {
data class Navigate(val params: Any?): UiEvent()
}
Run Code Online (Sandbox Code Playgroud)
ViewModel像这样修改你的
class MainViewModel : ViewModel() {
...
private val _oneTimeEvent = MutableSharedFlow<UiEvent>()
val oneTimeEvent = _oneTimeEvent.asSharedFlow()
...
fun navigate() {
if (_state.value == 1) {
viewModelScope.launch {
_oneTimeEvent.emit(UiEvent.Navigate(1))
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
,然后通过LaunchedEffect在你的FirstScreen
@Composable
fun FirstScreen(
navigate: () -> Unit,
..
) {
...
...
LaunchedEffect(Unit) {
mainViewModel.oneTimeEvent.collectLatest { uiEvent ->
when (uiEvent) {
is UiEvent.Navigate -> {
navigate()
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1781 次 |
| 最近记录: |