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

يمكنك استخدام مكتباتك المفضّلة في ميزة "الإنشاء". يوضِّح هذا القسم كيفية دمج بعض المكتبات الأكثر فائدة.

النشاط

لاستخدام 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 عقد لأذونات متعددة.

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

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

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

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

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

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

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

لمزيد من المعلومات عن ViewModel في Compose وكيفية استخدام النُسخ مع مكتبة Navigation Compose أو الأنشطة والمقاطع، اطّلِع على مستندات التشغيل التفاعلي.

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

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

  • تم تضمين LiveData.observeAsState() في العنصر androidx.compose.runtime:runtime-livedata:$composeVersion.
  • لا يتطلب Flow.collectAsState() استخدام أيّ مكتبات إضافية.
  • Observable.subscribeAsState() مضمّنة في عنصر androidx.compose.runtime:runtime-rxjava2:$composeVersion أو androidx.compose.runtime:runtime-rxjava3:$composeVersion

يتم تسجيل هذه العناصر كمستمع وتمثيل القيم على أنّها 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)
    }
}

العمليات غير المتزامنة في ميزة "الإنشاء"

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

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

يقدّم مكوّن التنقّل دعمًا لتطبيقات Jetpack Compose. اطّلِع على التنقّل باستخدام Compose و نقل بيانات واجهة مستخدم التنقّل في Jetpack إلى واجهة 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 وNavigation

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

رائع

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

Kotlin

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

عند استخدام ميزة "إنشاء مسار تنقّل"، استخدِم دائمًا الدالة القابلة للتجميع hiltViewModel للحصول على مثيل من @HiltViewModel ViewModel المُشارَك عليه تعليقات توضيحية. ويعمل هذا مع الأجزاء أو الأنشطة التي تمّت إضافة تعليقات توضيحية إليها باستخدام الرمز @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)
            }
        }
    }
}

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

تسهِّل عليك مكتبة الفهرسة carregar تحميل البيانات تدريجيًا، وهي متوافقة مع ميزة "الإنشاء". تحتوي صفحة إصدار الفهرسة على معلومات عن تبعية 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")
        }
    }
}

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

Maps

يمكنك استخدام مكتبة إنشاء خرائط لتوفير "خرائط 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"
        )
    }
}