Korzystanie z widoków w sekcji Utwórz

W interfejsie Compose możesz uwzględnić hierarchię widoków Androida. To podejście jest szczególnie przydatne, jeśli chcesz używać elementów interfejsu, które nie są jeszcze dostępne w Compose, np. AdView. Dzięki temu podejściu możesz też ponownie wykorzystać niestandardowe widoki, które zostały zaprojektowane.

Aby uwzględnić element widoku lub hierarchię, użyj komponentu AndroidView . AndroidView przekazywana jest lambda, która zwraca View. AndroidView udostępnia też wywołanie zwrotne update, które jest wywoływane, gdy widok jest rozwijany. AndroidView ponownie tworzy kompozycję, gdy zmieni się State odczytany w wywołaniu zwrotnym. AndroidView, podobnie jak wiele innych wbudowanych komponentów, przyjmuje parametr Modifier, którego można użyć np. do ustawienia jego pozycji w komponencie nadrzędnym.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableIntStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidViewAndroidView

AndroidView z powiązaniem widoku

Aby osadzić układ XML, użyj interfejsu AndroidViewBinding API, który jest udostępniany przez bibliotekę androidx.compose.ui:ui-viewbinding. Aby to zrobić, musisz włączyć powiązanie widoku w projekcie.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView na listach leniwych

Jeśli używasz AndroidView na liście leniwej (LazyColumn, LazyRow, Pager, itp.), rozważ użycie AndroidView przeciążenia wprowadzonego w wersji 1.4.0-rc01. To przeciążenie umożliwia Compose ponowne użycie bazowej instancji View, gdy kompozycja zawierająca jest ponownie używana, tak jak w przypadku list leniwych.

To przeciążenie AndroidView dodaje 2 dodatkowe parametry:

  • onReset – wywołanie zwrotne wywoływane w celu zasygnalizowania, że View ma zostać ponownie użyty. Aby umożliwić ponowne użycie widoku, musi to być wartość inną niż null.
  • onRelease (opcjonalne) – wywołanie zwrotne wywoływane w celu zasygnalizowania, że View opuścił kompozycję i nie będzie już ponownie używany.

@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

Fragmenty w Compose (etap przejściowy)

Aby dodać Fragment w Compose, użyj komponentu AndroidFragment. AndroidFragment ma obsługę specyficzną dla fragmentów, np. usuwanie fragmentu, gdy komponent opuszcza kompozycję.

Aby uwzględnić fragment, użyj AndroidFragment komponentu. Do AndroidFragment przekazujesz klasę Fragment, która następnie dodaje instancję tej klasy bezpośrednio do kompozycji. AndroidFragment udostępnia też obiekt fragmentState, który umożliwia utworzenie AndroidFragment z danym stanem, arguments do przekazania do nowego fragmentu oraz wywołanie zwrotne onUpdate, które udostępnia fragment z kompozycji. Podobnie jak wiele innych wbudowanych komponentów, AndroidFragment akceptuje parametr Modifier, którego można użyć np. do ustawienia jego pozycji w komponencie nadrzędnym.

Wywołaj AndroidFragment w Compose w ten sposób:

@Composable
fun FragmentInComposeExample() {
    AndroidFragment<MyFragment>()
}

Wywoływanie platformy Androida z Compose

Compose działa w klasach platformy Androida. Jest on np. hostowany w klasach widoków Androida, takich jak Activity czy Fragment, i może używać klas platformy Androida, takich jak Context, zasoby systemowe, Service czy BroadcastReceiver.

Więcej informacji o zasobach systemowych znajdziesz w artykule Zasoby w Compose.

Lokale kompozycji

CompositionLocal klasy umożliwiają niejawne przekazywanie danych przez funkcje komponentów. Zwykle są one udostępniane z wartością w określonym węźle drzewa interfejsu. Wartość ta może być używana przez komponenty potomne bez deklarowania CompositionLocal jako parametru w funkcji typu „composable”.

CompositionLocal służy do propagowania wartości typów platformy Androida w Compose, takich jak Context, Configuration czy View, w których hostowany jest kod Compose, z odpowiednimi LocalContext, LocalConfiguration, lub LocalView. Pamiętaj, że CompositionLocal klasy mają przedrostek Local, aby ułatwić ich znajdowanie za pomocą autouzupełniania w IDE.

Aby uzyskać dostęp do bieżącej wartości CompositionLocal, użyj jej właściwości current. Na przykład poniższy kod wyświetla komunikat toast, przekazując LocalContext.current do metody Toast.makeToast.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

Odbiorniki

Aby zaprezentować CompositionLocal i efekty uboczne, jeśli BroadcastReceiver musi zostać zarejestrowany z funkcji typu „composable”, użyj LocalContext, aby użyć bieżącego kontekstu, oraz rememberUpdatedState i DisposableEffect efektów ubocznych.

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

Inne interakcje

Jeśli nie ma narzędzia zdefiniowanego dla potrzebnej interakcji, najlepszym rozwiązaniem jest przestrzeganie ogólnej zasady Compose: dane przepływają w dół, a zdarzenia w górę (omówionej bardziej szczegółowo w artykule Myślenie w Compose). Na przykład ten komponent uruchamia inną aktywność:

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}