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

يمكنك استخدام مكتباتك المفضّلة في 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 لأذونات متعددة.

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

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

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

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

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

ViewModel

إذا كنت تستخدم مكتبة Architecture Components 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 حاليًا أو تنشئ عنصرًا جديدًا. By default, the returned ViewModel is scoped to the enclosing activity, fragment or navigation destination, and is retained as long as the scope is alive.

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

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

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