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

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

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

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

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

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

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

‫AndroidView ברשימות Lazy

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

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

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

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

מקטעים בפיתוח נייטיב (שלב מעבר)

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

כדי לכלול מקטע, משתמשים בקוד AndroidFragment קומפוזבילי. מעבירים מחלקת Fragment אל AndroidFragment, שמוסיפה מופע של המחלקה הזו ישירות לקומפוזיציה. ‫AndroidFragment מספק גם אובייקט fragmentState כדי ליצור את AndroidFragment עם מצב נתון, arguments להעברה ל-Fragment החדש וקריאה חוזרת (callback) של onUpdate שמספקת את ה-Fragment מהקומפוזיציה. כמו הרבה רכיבים קומפוזביליים מובְנים אחרים, AndroidFragment מקבל פרמטר Modifier שאפשר להשתמש בו, למשל, כדי להגדיר את המיקום שלו ברכיב הקומפוזבילי הראשי.

קוראים ל-AndroidFragment ב-Compose באופן הבא:

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

הגבלת המצב של מחזור החיים של המקטע

במקטע 1.9.0 ואילך, אפשר להשתמש בפרמטר maxLifecycle כדי להגביל את מצב מחזור החיים של ה-Fragment המוטמע.

לדוגמה, ב-HorizontalPager, אפשר להגדיר באופן דינמי את maxLifecycle לערך RESUMED רק כשהדף התואם פעיל, ולהגביל את הדפים שמחוץ למסך לערך STARTED. כשהמשתמש עובר לדף אחר, מכסת הפרגמנטים של הדף הקודם הופכת ל-STARTED. הפעולה הזו משהה או מבטלת עומסי עבודה שמוגדרים ל-RESUMED (כמו פונקציות coroutine שפועלות ב-repeatOnLifecycle(RESUMED)), ומונעת מדפים מחוץ למסך לבצע עבודה בחזית בלבד, תוך שמירה על ממשק המשתמש שלהם מאותחל ב-STARTED:

HorizontalPager(state = pagerState) { page ->
    // Dynamically cap the lifecycle state based on whether the page is selected
    val maxLifecycle = if (pagerState.settledPage == page) {
        Lifecycle.State.RESUMED
    } else {
        Lifecycle.State.STARTED
    }

    when (page) {
        0 -> AndroidFragment<HomeFragment>(maxLifecycle = maxLifecycle)
        1 -> AndroidFragment<LibraryFragment>(maxLifecycle = maxLifecycle)
        /* Other pages and corresponding fragments */
    }
}

קריאה למסגרת Android מ-Compose

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

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

Composition Locals

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

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

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

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

מקלטי שידורים

כדי להציג את 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 */
}

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

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

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