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

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

כדי לכלול רכיב תצוגה או היררכיה, צריך להשתמש בAndroidView קומפוזבילי. הפונקציה AndroidView מועברת lambda שמחזירה View. AndroidView מספק גם update קריאה חוזרת (callback) שנשלחת כאשר התצוגה מנופחת. היצירה מחדש של AndroidView בכל פעם שקריאה של State במסגרת הקריאה החוזרת משתנה. 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 צריך להשתמש AndroidViewBinding API, שסופק על ידי הספרייה androidx.compose.ui:ui-viewbinding. שפת תרגום לעשות זאת, הפרויקט צריך להפעיל קישור תצוגה (view Link).

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

AndroidView ברשימות של עצלנים

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

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

  • onReset - בוצעה קריאה חוזרת (callback) כדי לאותת שה-View עומד להתחיל שנעשה בו שימוש חוזר. הערך חייב להיות אפס כדי לאפשר שימוש חוזר בתצוגה מפורטת.
  • onRelease (אופציונלי) – בוצעה קריאה חוזרת כדי לסמן שה-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()
                }
            )
        }
    }
}

מקטעים ב'פיתוח'

צריך להשתמש בתוכן הקומפוזבילי AndroidViewBinding כדי להוסיף Fragment לכתיבה. ב-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 מהכתיבה

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

למידע נוסף על משאבי המערכת, ראו משאבים בכתיבה.

תושבי היצירה

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

CompositionLocal משמש להפצת ערכים לסוגי framework של Android ב- כתיבה כמו Context, Configuration או View שבה מתארח 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")
    }
}

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

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

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

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

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

דוגמה מציאותית יותר לתכונות שכדאי להעביר או להטמיע בחלונית הכתיבה, וכדי להציג את 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 ליכולת פעולה הדדית כשאתם משתמשים ב'כתיבה' בתצוגות אחרות' לחלופין, אפשר לעיין בדף שיקולים אחרים כדי לקבל מידע נוסף.