שימוש בתצוגות מפורטות בכתיבה

אפשר לכלול היררכיית View של Android בממשק המשתמש של Compose. הגישה הזו שימושית במיוחד אם רוצים להשתמש ברכיבי ממשק משתמש שעדיין לא זמינים ב-Compose, כמו AdView. הגישה הזו גם מאפשרת לכם לעשות שימוש חוזר בתצוגות בהתאמה אישית שעיצבתם.

כדי לכלול רכיב תצוגה או היררכיה, צריך להשתמש בתוכן הקומפוזבילי AndroidView . ל-AndroidView מועברת פונקציית lambda שמחזירה View. AndroidView מספק גם פונקציית קריאה חוזרת (callback) מסוג update שנקראת כשהתצוגה מורחבת. ה-AndroidView יתבצע מחדש בכל פעם שקריאת ה-State בתוך קריאת החזרה (callback) תשתנה. AndroidView, כמו רכיבים רבים אחרים מובנים, מקבל פרמטר Modifier שאפשר להשתמש בו, למשל, כדי להגדיר את המיקום שלו ברכיב ההורה.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableStateOf(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()
    }
}

AndroidView עם קישור תצוגה

כדי להטמיע פריסה של XML, משתמשים ב-API‏ AndroidViewBinding שמסופק על ידי הספרייה androidx.compose.ui:ui-viewbinding. כדי לעשות זאת, צריך להפעיל בפרויקט את קישור התצוגה המפורטת.

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

AndroidView ברשימות 'לאט'

אם אתם משתמשים ב-AndroidView ברשימה 'עצמה' (LazyColumn, LazyRow, Pager וכו'), כדאי להשתמש בעומס היתר של AndroidView שהושק בגרסה 1.4.0-rc01. עומס יתר זה מאפשר ל-Compose לעשות שימוש חוזר במכונה הבסיסית של View כשנעשה שימוש חוזר בהרכבה המכילה, כמו במקרה של רשימות 'לא עצלניות'.

לעומס יתר הזה של AndroidView יש עוד 2 פרמטרים:

  • onReset – קריאה חוזרת (callback) שמפעילים כדי לסמן שה-View עומד לשימוש חוזר. כדי לאפשר שימוש חוזר בתצוגה, הערך הזה לא יכול להיות null.
  • onRelease (אופציונלי) – בוצעה קריאה חוזרת (callback) כדי לסמן שה-View יצא מהיצירה ולא ייעשה בו שימוש חוזר.

@OptIn(ExperimentalComposeUiApi::class)
@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()
                }
            )
        }
    }
}

קטעים ב-Compose

משתמשים ב-composable ‏AndroidViewBinding כדי להוסיף Fragment ב-Compose. ל-AndroidViewBinding יש טיפול ספציפי למקטעים, כמו הסרת המקטע כשהתוכן הקומפוזבילי משאיר את הקומפוזביליות.

כדי לעשות זאת, צריך לנפח קובץ XML שמכיל FragmentContainerView בתור המאגר של Fragment.

לדוגמה, אם הגדרתם את my_fragment_layout.xml, תוכלו להשתמש בקוד כזה ולהחליף את מאפיין ה-XML android:name בשם הכיתה של Fragment:

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

אפשר לנפח את המקטע הזה ב-Compose (ראשי) באופן הבא:

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

אם אתם צריכים להשתמש בכמה קטעים באותו פריסה, חשוב לוודא שהגדרתם מזהה ייחודי לכל FragmentContainerView.

שליחת קריאה ל-framework של Android מהכתיבה

Compose פועל בתוך הכיתות של מסגרת Android. לדוגמה, הוא מתארח במחלקות של Android View, כמו Activity או Fragment, ועשוי להשתמש במחלקות framework של Android כמו Context, משאבי מערכת, Service או BroadcastReceiver.

מידע נוסף על משאבי המערכת זמין במאמר משאבים ב-Compose.

רכיבים מקומיים של יצירה מוזיקלית

מחלקות CompositionLocal מאפשרות להעביר נתונים באופן מרומז באמצעות פונקציות קומפוזביליות. בדרך כלל מציינים להם ערך בצומת מסוים בעץ של ממשק המשתמש. הצאצאים הקומפוזביליים שלו יכולים להשתמש בערך הזה בלי להצהיר על CompositionLocal כפרמטר בפונקציה הקומפוזבילית.

CompositionLocal משמש להפצת ערכים של סוגי מסגרות של Android ב-Compose, כמו Context,‏ Configuration או View שבו מתארח קוד Compose עם LocalContext,‏ LocalConfiguration או LocalView התואם. הערה: לכיתות CompositionLocal מצורף הקידומת Local כדי לשפר את הגילוי שלהן באמצעות ההשלמה האוטומטית בסביבת הפיתוח המשולבת (IDE).

גישה לערך הנוכחי של CompositionLocal באמצעות המאפיין current. לדוגמה, הקוד הבא מציג הודעת טוסטים על ידי העברת הערך LocalContext.current לשיטה Toast.makeToast.

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

לדוגמה מלאה יותר, אפשר לעיין בקטע Case Study: BroadcastReceivers בסוף המסמך הזה.

אינטראקציות אחרות

אם לא מוגדרת תוכנית שירות לאינטראקציה הרצויה, מומלץ לפעול לפי ההנחיה הכללית של Compose: הנתונים זורמים למטה, האירועים זורמים למעלה (הנושא הזה מוסבר בהרחבה במאמר חשיבה ב-Compose). לדוגמה, התוכן הקומפוזבילי הזה מפעיל פעילות אחרת:

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)
    }
}

מקרה לדוגמה: מקלטי שידורים

כדי להציג דוגמה ריאליסטית יותר לתכונות שאפשר להעביר או להטמיע ב-Compose, ולראות את CompositionLocal ואת האפקטים המשניים, נניח שצריך לרשום BroadcastReceiver מפונקציה שניתנת ליצירה.

הפתרון משתמש ב-LocalContext כדי להשתמש בהקשר הנוכחי, ובתופעות הלוואי של rememberUpdatedState ו-DisposableEffect.

@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 */
}

השלבים הבאים

עכשיו, אחרי שסיפרנו לכם על ממשקי ה-API לתאימות הדדית כשמשתמשים ב-Compose ב-Views ולהפך, כדאי לעיין בדף שיקולים נוספים כדי לקבל מידע נוסף.