So erhalten Sie Aktivität in Composing

Lesezeit: 4 Minuten

Gibt es eine Möglichkeit, die aktuelle Aktivität in der Compose-Funktion abzurufen?

@Composable
fun CameraPreviewScreen() {
    val context = ContextAmbient.current
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.CAMERA
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        ActivityCompat.requestPermissions(
            this, MainActivity.REQUIRED_PERMISSIONS, MainActivity.REQUEST_CODE_PERMISSIONS  // get activity for `this`
        )
        return
    }
}

Während die vorherige Antwort (das ist ContextWrapper-aware) in der Tat die richtige ist, möchte ich eine idiomatischere Implementierung zum Kopieren und Einfügen bereitstellen.

fun Context.getActivity(): AppCompatActivity? = when (this) {
    is AppCompatActivity -> this
    is ContextWrapper -> baseContext.getActivity()
    else -> null
}

Wie ContextWrappers können sich unmöglich gegenseitig umwickeln von Bedeutung Rekursion ist hier in Ordnung.

  • Nur das Sahnehäubchen in Form von zwei Vorschlägen zu dieser Antwort, die mir sehr gut gefallen: 1. markieren Sie es mit tailrec 2. es könnte geschrieben werden als a val Erweiterung: privater Tailrec-Spaß Context.getActivity(): AppCompatActivity? = when (this) { is AppCompatActivity -> this is ContextWrapper -> baseContext.getActivity() else -> null } val Context.activity: AppCompatActivity? get() = getActivity()

    – Alex Facciorusso

    13. August 2021 um 20:24 Uhr


  • Ich fühle mich wirklich dumm, aber das hat bei mir nicht funktioniert, weil meine Aktivität so war Activity Anstatt von AppCompatActivity. Stellen Sie also sicher, dass Sie den richtigen Typ haben.

    – Jack

    9. Februar um 4:09

  • is ContextWrapper -> baseContext.getActivity() ist ein rekursiver Aufruf.

    – Psijik

    5. Mai um 14:10 Uhr

Um den Kontext zu bekommen

val context = LocalContext.current

Rufen Sie dann die Aktivität mithilfe des Kontexts ab. Erstellen Sie eine Erweiterungsfunktion und rufen Sie diese Erweiterungsfunktion mit Ihrem Kontext wie auf context.getActivity().

fun Context.getActivity(): AppCompatActivity? {
  var currentContext = this
  while (currentContext is ContextWrapper) {
       if (currentContext is AppCompatActivity) {
            return currentContext
       }
       currentContext = currentContext.baseContext
  }
  return null
}

Benutzer-Avatar
Abdelila El Aissaoui

Sie können die Aktivität aus Ihren Composables abrufen, indem Sie den Kontext umwandeln (ich habe keinen einzigen Fall gefunden, in dem der Kontext nicht die Aktivität war). Wie Jim jedoch erwähnt hat, ist dies keine gute Praxis.

val activity = LocalContext.current as Activity

Persönlich verwende ich es, wenn ich nur mit Code herumspiele, der die Aktivität erfordert (Berechtigungen sind ein gutes Beispiel), aber sobald ich es zum Laufen gebracht habe, verschiebe ich es einfach in die Aktivität und verwende Parameter/Callback.

Bearbeiten: Wie in den Kommentaren erwähnt, kann die Verwendung im Produktionscode gefährlich sein, da es abstürzen kann, da Current ein Kontext-Wrapper ist. Mein Vorschlag dient hauptsächlich zum Testen von Code.

  • Dies könnte abstürzen. Kontext ist in diesem Fall nicht unbedingt eine Aktivität, es kann ein ContextWrapper sein.

    – Dimezis

    3. Dezember 2021 um 11:11 Uhr


Verwenden Sie zum Anfordern der Laufzeitberechtigung in Jetpack Compose die Begleitbibliothek: https://github.com/google/accompanist/tree/main/permissions

Anwendungsbeispiel aus Dokumente:

@Composable
private fun FeatureThatRequiresCameraPermission(
    navigateToSettingsScreen: () -> Unit
) {
    // Track if the user doesn't want to see the rationale any more.
    var doNotShowRationale by rememberSaveable { mutableStateOf(false) }

    val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
    PermissionRequired(
        permissionState = cameraPermissionState,
        permissionNotGrantedContent = {
            if (doNotShowRationale) {
                Text("Feature not available")
            } else {
                Column {
                    Text("The camera is important for this app. Please grant the permission.")
                    Spacer(modifier = Modifier.height(8.dp))
                    Row {
                        Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
                            Text("Ok!")
                        }
                        Spacer(Modifier.width(8.dp))
                        Button(onClick = { doNotShowRationale = true }) {
                            Text("Nope")
                        }
                    }
                }
            }
        },
        permissionNotAvailableContent = {
            Column {
                Text(
                    "Camera permission denied. See this FAQ with information about why we " +
                        "need this permission. Please, grant us access on the Settings screen."
                )
                Spacer(modifier = Modifier.height(8.dp))
                Button(onClick = navigateToSettingsScreen) {
                    Text("Open Settings")
                }
            }
        }
    ) {
        Text("Camera permission Granted")
    }
}

Wenn Sie die Quelle überprüfen, werden Sie das auch herausfinden Google verwendet dieselbe Problemumgehung wie in der Antwort von Rajeev angegeben, daher ist Jims Antwort auf schlechte Praktiken etwas umstritten.

Anstatt die zu werfen Context zu einem Activitykönnen Sie es sicher verwenden, indem Sie eine erstellen LocalActivity.

val LocalActivity = staticCompositionLocalOf<ComponentActivity> {
    noLocalProvidedFor("LocalActivity")
}

private fun noLocalProvidedFor(name: String): Nothing {
    error("CompositionLocal $name not present")
}

Verwendungszweck:

CompositionLocalProvider(LocalActivity provides this) {
   val activity = LocalActivity.current
   // your content
} 

Benutzer-Avatar
Jim

Das Abrufen einer Aktivität aus einer Composable-Funktion gilt als schlechte Vorgehensweise, da Ihre Composables nicht eng mit dem Rest Ihrer App gekoppelt sein sollten. Unter anderem wird eine enge Kopplung Sie daran hindern, Ihr Composable zu testen, und die Wiederverwendung im Allgemeinen erschweren.

Wenn Sie sich Ihren Code ansehen, sieht es so aus, als würden Sie Berechtigungen innerhalb des Composable anfordern. Auch dies möchten Sie nicht in Ihrem Composable tun, da Composable-Funktionen so oft wie in jedem Frame ausgeführt werden können, was bedeutet, dass Sie diese Funktion in jedem Frame aufrufen würden.

Richten Sie stattdessen Ihre Kameraberechtigungen in Ihrer Aktivität ein und geben Sie (über Parameter) alle Informationen weiter, die von Ihrem Composable benötigt werden, um Pixel zu rendern.

Benutzer-Avatar
Val Okafor

Unten finden Sie eine geringfügige Änderung der @Jeffset-Antwort, da Compose-Aktivitäten auf ComponentActivity und nicht auf AppCompatActivity basieren.

fun Context.getActivity(): ComponentActivity? = when (this) {
    is ComponentActivity -> this
    is ContextWrapper -> baseContext.findActivity()
    else -> null
}

1017420cookie-checkSo erhalten Sie Aktivität in Composing

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy