我的应用程序上有 3 个屏幕(
MainScreen
、Screen1
和 Screen2
)。
MainScreen
上有两个按钮,用于在其他屏幕上导航。
这是当前的应用程序:
我的问题是,如果我快速单击两个按钮,两个目的地都会打开。我不想要这种行为,我只想打开我首先单击的按钮的屏幕。 换句话说,
MainScreen
必须始终保留在后台,并且Screen1
上方只能有两个屏幕之一(Screen2
,MainScreen
)。如何做到这一点?
这是代码:
MainActivity.kt
enum class Routes {
MainScreen, Screen1, Screen2
}
sealed class UiEvent {
data class Navigate(val route: String): UiEvent()
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestTheme {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Routes.MainScreen.name
) {
composable(route = Routes.MainScreen.name) {
MainScreen(onNavigate = {
navController.navigate(route = it.route) {
launchSingleTop = true
}
},)
}
composable(route = Routes.Screen1.name) {
Screen1()
}
composable(route = Routes.Screen2.name) {
Screen2()
}
}
}
}
}
}
@Composable
fun MainScreen(
onNavigate: (UiEvent.Navigate) -> Unit,
viewModel: MainViewModel = hiltViewModel()
) {
LaunchedEffect(key1 = true) {
viewModel.uiEvent.collectLatest { uiEvent ->
when (uiEvent) {
is UiEvent.Navigate -> {
onNavigate(uiEvent)
}
}
}
}
Row {
Button(onClick = { viewModel.navigateTo(Routes.Screen1) }) {
Text(text = "Screen1")
}
Button(onClick = { viewModel.navigateTo(Routes.Screen2) }) {
Text(text = "Screen2")
}
}
}
@Composable
fun Screen1() { Text(text = "Screen 1") }
@Composable
fun Screen2() { Text(text = "Screen 2") }
MainViewModel.kt
@HiltViewModel
class MainViewModel @Inject constructor() : ViewModel() {
private val _uiEvent = Channel<UiEvent>()
val uiEvent = _uiEvent.receiveAsFlow()
fun navigateTo(route: Routes) {
sendUiEvent(UiEvent.Navigate(route.name))
}
private fun sendUiEvent(event: UiEvent) {
viewModelScope.launch {
_uiEvent.send(event)
}
}
}
提前致谢。
您可以创建一个不允许在短时间内执行多次单击的按钮包装。
@Composable
fun SingleClickableComposableWrapper(
onClick: (action: Int) -> Unit,
content: @Composable (onClick: SingleClickHandler) -> Unit
) {
val clickAction = remember(onClick) {
SingleClickHandler(onClick = onClick)
}
content(clickAction)
}
然后像这样创建 SingleClickHandler:
class SingleClickHandler(private val onClick: (action: Int) -> Unit) {
private val actionDisabledDuration = 650L
private var previousActionTimestamp = 0L
fun handleAction(action: Int = -1) {
val currentTimestamp = Instant.now().toEpochMilli()
val millisDiff = currentTimestamp - previousActionTimestamp
previousActionTimestamp = currentTimestamp
if (millisDiff > actionDisabledDuration) {
onClick(action)
}
}
}
现在您可以使用 SingleClickableComposableWrapper 包装按钮并像这样使用它:
SingleClickableComposableWrapper(
onClick = { action ->
//here you can check the action and call the navigation that you want to do.
when(action) {
0 -> viewModel.navigateTo(Routes.Screen1)
1 -> viewModel.navigateTo(Routes.Screen2)
else -> //wrong action code
}
}
) { clickHandler ->
//Here you can put your buttons and pass in onClick the clickHandler
Row {
Button(onClick = { clickHandler.handleAction(0) }) {
Text(text = "Screen1")
}
Button(onClick = { clickHandler.handleAction(1) }) {
Text(text = "Screen2")
}
}
}
这似乎是一个复杂的解决方案,但如果您设置了 SingleClickableComposableWrapper 和 SingleClickHandler,那么它将是可重用的。避免双击同一组件也很有帮助,这种情况很多时候都会对您的问题产生相同的效果。