الإنشاء والمكتبات الأخرى

يمكنك استخدام المكتبات المفضّلة لديك في Compose. يوضّح هذا القسم كيفية دمج بعض المكتبات الأكثر فائدة.

النشاط

لاستخدام Compose في نشاط، يجب استخدام ComponentActivity، وهو فئة فرعية من Activity توفّر LifecycleOwner والمكوّنات المناسبة لـ Compose. وتوفّر أيضًا واجهات برمجة تطبيقات إضافية تفصل الرمز البرمجي عن طرق التجاوز في فئة النشاط. تعرض Activity Compose واجهات برمجة التطبيقات هذه للعناصر القابلة للإنشاء، وبالتالي لن يكون عليك بعد الآن إلغاء الطرق خارج العناصر القابلة للإنشاء أو استرداد مثيل Activity صريح. علاوةً على ذلك، تضمن واجهات برمجة التطبيقات هذه عدم تهيئتها إلا مرة واحدة، وأنّها تظل متاحة بعد إعادة التركيب، وأنّها تُنظَّف بشكل صحيح في حال تمت إزالة العنصر القابل للإنشاء من التركيب.

نتيجة النشاط

تتيح لك واجهة برمجة التطبيقات rememberLauncherForActivityResult() إمكانية الحصول على نتيجة من نشاط في الدالة المركّبة:

@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(). وهذا يعني أنّه يمكنك استخدام هذا الأسلوب لطلب محتوى من إطار العمل وفي أنماط شائعة أخرى. يمكنك أيضًا إنشاء عقود مخصّصة واستخدامها مع هذا الأسلوب.

طلب أذونات وقت التشغيل

يمكن استخدام واجهة برمجة التطبيقات Activity Result API وrememberLauncherForActivityResult() الموضّحة أعلاه من أجل طلب أذونات في وقت التشغيل باستخدام العقد RequestPermission للحصول على إذن واحد أو العقد RequestMultiplePermissions للحصول على أذونات متعددة.

يمكن أيضًا استخدام مكتبة أذونات تطبيق مصاحب كطبقة فوق واجهات برمجة التطبيقات هذه لربط حالة الأذونات الممنوحة حاليًا بالحالة التي يمكن أن تستخدمها واجهة مستخدم Compose.

التعامل مع زر الرجوع في النظام

لتوفير عملية تنقّل مخصّصة للرجوع وتجاوز السلوك التلقائي لزر الرجوع في النظام من داخل العنصر القابل للإنشاء، يمكن أن يستخدم العنصر القابل للإنشاء BackHandler لاعتراض هذا الحدث:

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

يتحكّم الوسيط الأول في ما إذا كان BackHandler مفعّلاً حاليًا. يمكنك استخدام هذا الوسيط لإيقاف المعالج مؤقتًا استنادًا إلى حالة المكوّن. سيتم استدعاء دالة lambda اللاحقة إذا فعّل المستخدم حدث الرجوع إلى الخلف في النظام، وكان BackHandler مفعّلاً حاليًا.

ViewModel

إذا كنت تستخدم مكتبة ViewModel في "مكوّنات البنية"، يمكنك الوصول إلى ViewModel من أي دالة مركّبة من خلال استدعاء الدالة viewModel(). أضِف التبعية التالية إلى ملف Gradle:

أنيق

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

Kotlin

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

يمكنك بعد ذلك استخدام الدالة 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 في عناصر screen-level القابلة للإنشاء، أي بالقرب من عنصر أساسي قابل للإنشاء يتم استدعاؤه من نشاط أو جزء أو وجهة في الرسم البياني للتنقّل. ويرجع ذلك إلى أنّ نطاق ViewModels يكون تلقائيًا مقتصرًا على عناصر مستوى الشاشة هذه. يمكنك الاطّلاع على مزيد من المعلومات حول ViewModel دورة حياة ونطاق هنا.

حاوِل تجنُّب تمرير مثيلات ViewModel إلى عناصر أخرى قابلة للإنشاء، لأنّ ذلك قد يصعّب اختبار هذه العناصر القابلة للإنشاء وقد يؤدي إلى تعطيل المعاينات. بدلاً من ذلك، مرِّر البيانات والدوال التي تحتاج إليها فقط كمعلَمات.

يمكنك استخدام مثيلات ViewModel لإدارة الحالة في العناصر القابلة للإنشاء على مستوى الشاشة الفرعية، ولكن عليك الانتباه إلى دورة حياة ونطاق ViewModel. إذا كانت الدالة المركّبة مكتفية ذاتيًا، يمكنك استخدام Hilt لإدخال ViewModel وتجنُّب ضبط التبعيات من الدوال المركّبة الرئيسية.

إذا كان ViewModel يتضمّن تبعيات، تأخذ viewModel() قيمة اختيارية ViewModelProvider.Factory كمَعلمة.

لمزيد من المعلومات حول ViewModel في Compose وكيفية استخدام العناصر مع مكتبة Navigation Compose أو الأنشطة والتقسيمات، راجِع مستندات التشغيل التفاعلي.

مصادر البيانات

تتضمّن Compose إضافات لأكثر الحلول المستندة إلى البث شيوعًا في Android. يتم توفير كل من هذه الإضافات من خلال عنصر مختلف:

تسجّل هذه العناصر كعناصر مستمعة وتمثّل القيم كـ State. عندما يتم إصدار قيمة جديدة، يعيد Compose إنشاء أجزاء واجهة المستخدم التي يتم فيها استخدام 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)
    }
}

العمليات غير المتزامنة في Compose

يتيح لك Jetpack Compose تنفيذ عمليات غير متزامنة باستخدام الروتينات المشتركة من داخل العناصر القابلة للإنشاء.

يمكنك الاطّلاع على واجهات برمجة التطبيقات LaunchedEffect وproduceState وrememberCoroutineScope في مستندات الآثار الجانبية للحصول على مزيد من المعلومات.

يتيح مكوّن التنقّل استخدام تطبيقات Jetpack Compose. لمزيد من المعلومات، يُرجى الاطّلاع على التنقّل باستخدام Compose ونقل Jetpack Navigation إلى Navigation Compose.

Hilt

‫Hilt هو الحلّ المقترَح لتوفير التبعية في تطبيقات Android، ويتوافق بسلاسة مع Compose.

تستخدم الدالة viewModel() المذكورة في قسم ViewModel تلقائيًا ViewModel الذي ينشئه Hilt باستخدام التعليق التوضيحي @HiltViewModel. لقد قدّمنا مستندات تتضمّن معلومات حول دمج ViewModel في Hilt.

@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 والتنقّل

يتكامل Hilt أيضًا مع مكتبة Navigation Compose. أضِف الاعتمادات الإضافية التالية إلى ملف Gradle:

أنيق

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

Kotlin

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

عند استخدام Navigation Compose، استخدِم دائمًا الدالة 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. تحتوي صفحة إصدار الترحيل إلى نظام الصفحات على معلومات حول التبعية الإضافية paging-compose التي يجب إضافتها إلى المشروع وإصدارها.

في ما يلي مثال على واجهات برمجة التطبيقات الخاصة بمكتبة Paging والمتوافقة مع Compose:

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

يمكنك الاطّلاع على مستندات القوائم والجداول للحصول على مزيد من المعلومات حول استخدام ميزة "تقسيم المحتوى إلى صفحات" في Compose.

خرائط

يمكنك استخدام مكتبة Maps Compose لتوفير "خرائط 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 = remember { MarkerState(position = singapore) },
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}