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

يمكنك استخدام المكتبات المفضّلة لديك في 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 كطبقة أعلى من واجهات برمجة التطبيقات هذه لربط حالة الأذونات الممنوحة حاليًا بالحالة التي يمكن أن تستخدمها واجهة مستخدم Compose.

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

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

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

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

ViewModel

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

Groovy

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

Kotlin

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

يمكنك بعد ذلك استخدام الدالة viewModel() في الرمز البرمجي.

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

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

تعرض الدالة viewModel() ViewModel حاليًا أو تنشئ 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's ونطاقها هنا.

حاوِل تجنُّب تمرير مثيلات 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. لقد وفّرنا مستندات تتضمّن معلومات حول دمج Hilt مع 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 وNavigation

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

Groovy

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

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.2.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)
            }
        }
    }
}

ترقيم الصفحات

تسهّل مكتبة تقسيم الصفحات عليك تحميل البيانات تدريجيًا، وهي متوافقة مع Compose. تحتوي صفحة إصدار الترقيم إلى صفحات على معلومات حول التبعية الإضافية 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

يمكنك استخدام مكتبة 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"
        )
    }
}