כתיבה וספריות אחרות

אפשר להשתמש בספריות המועדפות עליך בקטע 'כתיבה'. בקטע הזה נסביר איך לכלול כמה מהספריות השימושיות ביותר.

פעילות

כדי להשתמש ב'כתיבה' בפעילות, צריך להשתמש ב- ComponentActivity תת-מחלקה של Activity שמספקת את ה-LifecycleOwner וגם של המפענח. הוא גם מספק ממשקי API נוספים שמפרידים את הקוד, משינוי שיטות בשיעור הפעילות. כתיבה של פעילות חושפת את ממשקי ה-API האלה לתכנים קומפוזביליים, כך שעוקפים שיטות מחוץ כבר אין צורך בתוכן קומפוזבילי או אחזור של מופע Activity בוטה. בנוסף, ממשקי ה-API האלה מבטיחים שהם יופעלו רק פעם אחת, והם ימשיכו להתקיים להרכיב מחדש, ולנקות כמו שצריך אם מסירים את התוכן הקומפוזבילי של משפטים יחידים,

תוצאת פעילות

rememberLauncherForActivityResult() API מאפשר לכם קבלת תוצאה מפעילות בתוכן הקומפוזבילי:

@Composable
fun GetContentExample() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column {
        Button(onClick = { launcher.launch("image/*") }) {
            Text(text = "Load Image")
        }
        Image(
            painter = rememberAsyncImagePainter(imageUri),
            contentDescription = "My Image"
        )
    }
}

הדוגמה הזו ממחישה GetContent() חוזה. הקשה על הלחצן מפעילה את הבקשה. ה-lambda בסוף עבור rememberLauncherForActivityResult() מופעלת ברגע שהמשתמש בוחר תמונה וחוזר לפעילות ההפעלה. הפעולה הזו טוענת את התמונה שנבחרה באמצעות rememberImagePainter() של Coil מותאמת אישית.

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

בקשת הרשאות בתחילת ההפעלה

אותו ממשק API של Activity result rememberLauncherForActivityResult() שהוסבר למעלה יכול לשמש בקשת הרשאות בתחילת ההפעלה באמצעות RequestPermission חוזה להרשאה יחידה או RequestMultiplePermissions בחוזה עם הרשאות מרובות.

ספריית ההרשאות של משתתפים אחרים אפשר גם להשתמש בשכבה מעל ממשקי ה-API האלה כדי למפות את מצב האישור הנוכחי את ההרשאות למצב שבו ממשק המשתמש של 'כתיבה' יכול להשתמש בהן.

טיפול בלחצן 'הקודם' של המערכת

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

var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
    // Handle back press
}

הארגומנט הראשון קובע אם BackHandler מופעלת כרגע; אפשר להשתמש בארגומנט הזה כדי להשבית באופן זמני את ה-handler בהתאם למצב הרכיב שלך. ה-lambda הסופי יופעל אם המשתמש יפעיל אירוע חזרה של המערכת, BackHandler מופעלת כרגע.

ViewModel

אם אתם משתמשים ברכיבי הארכיטקטורה אפשר לגשת לספריית ViewModel. ViewModel מכל תוכן קומפוזבילי מאת קוראים לפונקציה viewModel() מותאמת אישית. מוסיפים את יחסי התלות הבאים לקובץ Gradle:

מגניב

dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
}

Kotlin

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
}

לאחר מכן אפשר להשתמש בפונקציה viewModel() בקוד.

class MyViewModel : ViewModel() { /*...*/ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

הפונקציה viewModel() מחזירה ViewModel קיים או יוצרת מזהה חדש. כברירת מחדל, הערך של ViewModel שהוחזר הוא בהיקף של הפעילות המצורף, המקטע או יעד הניווט, ונשמר כל עוד ההיקף פעיל.

לדוגמה, אם משתמשים בתוכן הקומפוזבילי בפעילות, viewModel() מחזירה את הערך אותו מכונה עד שהפעילות תסתיים או עד שהתהליך מסתיים.

class MyViewModel : ViewModel() { /*...*/ }
// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyScreen2(
    viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }

הנחיות לשימוש

בדרך כלל אפשר לגשת ל-ViewModel מכונות ברמת המסך תכנים קומפוזביליים, כלומר, קרובים לתוכן קומפוזבילי ברמה הבסיסית (root) שנקראה מפעילות, שבר או יעד של תרשים ניווט. הסיבה לכך היא ש-ViewModel כברירת מחדל, בהיקף של האובייקטים ברמת המסך. מידע נוסף על של ViewModel כאן על מחזור החיים וההיקף.

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

אפשר להשתמש ב-ViewModel מכונות כדי לנהל את המצב של תכנים קומפוזביליים ברמת המסך המשני. עם זאת, חשוב לשים לב של ViewModel מחזור חיים והיקף. אם קומפוזבילי עצמאי, כדאי להשתמש ב-Hilt כדי להחדיר את ViewModel כדי לא להעביר יחסי תלות מההורה של תכנים קומפוזביליים.

אם יש יחסי תלות ב-ViewModel, הפונקציה viewModel() ViewModelProvider.Factory בתור פרמטר.

למידע נוסף על ViewModel בקטע 'כתיבה' ועל אופן השימוש במכונות באמצעות ספריית הניווט 'כתיבה', או פעילויות ומקטעים, כדאי לעיין במסמכים בנושא יכולת פעולה הדדית.

מקורות נתונים

ההצעות לכתיבה כוללות תוספים שמתאימים לפתרונות הכי פופולריים של Android שמבוססים על שידורים. כל אחד מהתוספים הבאים מסופק על ידי פריט מידע שנוצר בתהליך פיתוח (Artifact) אחר:

  • פריט LiveData.observeAsState() נכלל בפריטי המידע שנוצרו בתהליך הפיתוח (Artifact) של androidx.compose.runtime:runtime-livedata:$composeVersion.
  • Flow.collectAsState() לא מחייב יחסי תלות נוספים.
  • Observable.subscribeAsState() נכלל בפריטי מידע שנוצרו בתהליך הפיתוח (Artifact) מסוג androidx.compose.runtime:runtime-rxjava2:$composeVersion או androidx.compose.runtime:runtime-rxjava3:$composeVersion.

ארטיפקטים האלה נרשמים כמאזינים ומייצגים את הערכים State בכל פעם ערך חדש נוצר, 'כתיבה' מרכיב מחדש את החלקים של ממשק המשתמש שבהם ה-state.value בשימוש. לדוגמה, בקוד הזה, ShowData נוצר מחדש בכל פעם הערך exampleLiveData פולט ערך חדש.

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyScreen recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

פעולות אסינכרוניות בכתיבה

השירות 'Jetpack פיתוח נייטיב' מאפשר לבצע פעולות אסינכרוניות באמצעות קורוטין בתכנים הקומפוזביליים.

לעיון בממשקי ה-API של LaunchedEffect, produceState ו-rememberCoroutineScope ב- במסמכי התיעוד בנושא תופעות לוואי מידע.

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

ידית

Hilt הוא הפתרון המומלץ להזרקת תלות באפליקציות ל-Android. פועל בצורה חלקה עם 'כתיבה'.

הפונקציה viewModel() שמוזכרת בקטע ViewModel משתמשת אוטומטית ב-ViewModel, ש-Hilt בונה עם @HiltViewModel הערה. סיפקנו תיעוד עם מידע על ה-ViewModel של Hilt's ViewModel אינטגרציה.

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

הסתרה וניווט

Hilt משתלב גם עם ספריית הניווט Compose. מוסיפים את הטקסט הבא יחסי תלות נוספים לקובץ Gradle:

מגניב

dependencies {
    implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
}

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
}

כשמשתמשים בניסוח אוטומטי, צריך להשתמש תמיד בתוכן הקומפוזבילי hiltViewModel כדי לקבל מופע של ViewModel עם ההערה @HiltViewModel. התכונה פועלת עם פעילויות או מקטעים שמסומנים בהערות @AndroidEntryPoint

לדוגמה, אם ExampleScreen הוא יעד בתרשים ניווט, קוראים לפונקציה hiltViewModel() כדי לקבל מופע של ExampleViewModel אל היעד, כפי שמוצג בקטע הקוד הבא:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val viewModel = hiltViewModel<MyViewModel>()
            MyScreen(viewModel)
        }
        /* ... */
    }
}

אם אתם צריכים לאחזר את המופע של ViewModel בהיקף נתיבי ניווט או תרשים ניווט נשתמש בפונקציה הקומפוזבילית hiltViewModel ומעבירים את backStackEntry כפרמטר:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    val innerStartRoute = "exampleWithRoute"
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

חלוקה לדפים

החלוקה לדפים ספרייה מאפשר לטעון נתונים בהדרגה והוא נתמך בכתיבה. גרסת ההחלפה הדף מכיל מידע על התלות הנוספת של paging-compose שיש להוסיף לפרויקט ולגרסה שלו.

הנה דוגמה לממשק ה-API של 'פיתוח נייטיב' בספריית הדפים:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

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

מפות

אפשר להשתמש בתכונה כתיבה במפות כדי לספק את מפות Google באפליקציה שלכם. דוגמה לשימוש:

@Composable
fun MapsExample() {
    val singapore = LatLng(1.35, 103.87)
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(singapore, 10f)
    }
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState
    ) {
        Marker(
            state = MarkerState(position = singapore),
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}