Android Compose - 如何防止Dialog显示两次?

问题描述 投票:0回答:1

我试图在当前视图呈现后立即显示一个对话框。但是,该对话框显示了两次(可能是由于重组的工作原理)。

val checkGpsEnabled by viewModel.checkGpsEnabled.collectAsState()
val showGpsDisabledDialog by viewModel.showGpsDisabledDialog.collectAsState()
val locationManager = LocalContext.current.getSystemService(Context.LOCATION_SERVICE) as LocationManager

if (checkGpsEnabled) {
    val gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    viewModel.setGpsChecked()
    if (!gpsEnabled) {
        viewModel.openGpsDisabledDialog()
    }
}

Log.d(TAG, showGpsDisabledDialog.toString())
if (showGpsDisabledDialog) {
    Log.d(TAG, "dialog")
    val context = LocalContext.current

    TwoActionsDialog(
        onDismissRequest = {
            Log.d(TAG, "dismiss")
            viewModel.closeGpsDisabledDialog()
        },
        mainText = "Gps disabled",
        secondaryText = "Please enable location services.\nOtherwise, the app will not know where you took the photos.",
        primaryButtonText = "OK, go to settings",
        secondaryButtonText = "No, continue without location",
        onPrimaryButtonClick = {
            viewModel.closeGpsDisabledDialog()
            context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
        },
        onSecondaryButtonClick = {
            Log.d(TAG, "decline")
            viewModel.closeGpsDisabledDialog()
        }
    )
}

视图模型:

@HiltViewModel
class MapViewModel @Inject constructor(): ViewModel() {

private var _checkGpsEnabled: MutableStateFlow<Boolean> = MutableStateFlow(true)
val checkGpsEnabled: StateFlow<Boolean> get() = _checkGpsEnabled.asStateFlow()

private var _showGpsDisabledDialog: MutableStateFlow<Boolean> = MutableStateFlow(false)
val showGpsDisabledDialog: StateFlow<Boolean> get() = _showGpsDisabledDialog.asStateFlow()

fun setGpsChecked() {
    if (_checkGpsEnabled.value)
        _checkGpsEnabled.value = false
}

fun openGpsDisabledDialog() {
    if(!_showGpsDisabledDialog.value)
        _showGpsDisabledDialog.value = true
}

fun closeGpsDisabledDialog() {
    if(_showGpsDisabledDialog.value)
        _showGpsDisabledDialog.value = false
}

}

我尝试将状态存储在可组合项中,然后存储在视图模型中。 我尝试使用 LaunchedEffect 设置这些标志(因此它们在可组合渲染之前不是 true) 我尝试使用延迟在视图模型中将它们设置为 true。

我发现在所有这些情况下,通过渲染一个带有文本的简单对话框,对话框会显示两次,并注意到有两个文本重叠

android kotlin android-jetpack-compose compose-recomposition
1个回答
0
投票

你的代码非常复杂,很难理解。从上面的代码来看,我认为如果设备的 GPS 被禁用,您想显示一个对话框。

这是我的简单且可重复使用的方法:

上下文扩展:您可以编写以下扩展函数(最好将其放在项目中 utils 包下的单独文件中)。它返回一个回调流,流式传输 GPS 设置中的实时变化。

fun Context.observeGpsStatus(): Flow<Boolean> = callbackFlow {
    val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager

    val isGpsEnabled = {
        locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    }

    // Emit initial status
    trySend(isGpsEnabled())

    val gpsStatusReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent?) {
            trySend(isGpsEnabled())
        }
    }

    val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
    registerReceiver(gpsStatusReceiver, filter)

    awaitClose {
        unregisterReceiver(gpsStatusReceiver)
    }
}

GpsStatusObserver 可组合: 这是一个简单的可重用可组合项,可以集成到任何项目的任何屏幕中。

@Composable
fun GpsStatusObserver() {
    val context = LocalContext.current
    val gpsFlow = remember { context.observeGpsStatus() }
    val isGpsEnabled = gpsFlow.collectAsState(initial = true)

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) {}

    if (!isGpsEnabled.value) {
        PermissionDialog(
            title = stringResource(id = R.string.enable_gps),
            permissionTextProvider = GPSPermissionTextProvider(),
            isPermanentlyDeclined = true,
            onDismiss = {  },
            onOkClick = {  }
        ) {
            val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
            launcher.launch(intent)
        }
    }
}

然后只需在屏幕可组合项中调用它即可:

@Composable
fun ExampleScreen() {
    GpsStatusObserver()
    
    // other content of the screen
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.