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

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

כדי לכלול רכיב תצוגה או היררכיה, משתמשים ב-AndroidView composable. הפונקציה AndroidView מקבלת פונקציית למבדה שמחזירה View. ‫AndroidView מספק גם קריאה חוזרת (callback) של update שמופעלת כשהתצוגה מורחבת. הפונקציה AndroidView מורכבת מחדש בכל פעם שמשתנה ערך של State שנקרא בתוך פונקציית הקריאה החוזרת. ‫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 עם view binding

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

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

AndroidView ב-Lazy lists

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

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

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

מקטעים ב-Compose (שלב מעבר)

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

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

כדי להתקשר אל AndroidFragment ב-Compose, פועלים לפי השלבים הבאים:

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

הגבלת מצב מחזור החיים של רכיב Cap

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

לדוגמה, ב-HorizontalPager, אפשר להגדיר באופן דינמי את maxLifecycle ל-RESUMED רק כשהדף המתאים פעיל, וכך להגביל את הדפים שלא מוצגים במסך ל-STARTED. כשהמשתמש מנווט לדף אחר, מגבלת הפרגמנט של הדף הקודם הופכת ל-STARTED. הפעולה הזו משהה או מבטלת עומסי עבודה שמוגדרים בהיקף של RESUMED (כמו קורוטינות שפועלות ב-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 פועלת במסגרת המחלקות של Android. לדוגמה, הוא מתארח במחלקות של Android View, כמו Activity או Fragment, ועשוי להשתמש במחלקות של Android framework כמו 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")
    }
}

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

כדי להציג את 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). לדוגמה, הקומפוזיציה הזו מפעילה פעילות אחרת:

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