‫Compose و کتابخانه‌های دیگر

می‌توانید از کتابخانه‌های دلخواهتان در Compose استفاده کنید. این بخش نحوه ادغام چند مورد از مفیدترین کتابخانه‌ها را شرح می‌دهد.

فعالیت

برای استفاده از Compose در یک فعالیت، باید از ComponentActivity، یک زیرکلاس از Activity که LifecycleOwner و مؤلفه‌های مناسب را برای Compose فراهم می‌کند، استفاده کنید. همچنین میاناهای برنامه‌سازی کاربردی دیگری ارائه می‌دهد که کد شما را از روش‌های لغو در کلاس فعالیتتان جدا می‌کند. Activity Compose این میاناهای برنامه‌سازی کاربردی را برای composableها آشکار می‌کند به‌طوری‌که دیگر نیازی به روش‌های لغو کردن خارج از composableها یا بازیابی نمونه صریح Activity نیست. علاوه‌براین، این میاناهای برنامه‌سازی کاربردی تضمین می‌کنند که فقط یک‌بار مقداردهی اولیه شوند، از ترکیب‌بندی مجدد جان سالم به‌در ببرند، و اگر ترکیب‌شدنی از ترکیب‌بندی برداشته شد، به‌درستی پاک‌سازی شوند.

نتیجه فعالیت

میانای برنامه‌سازی کاربردی rememberLauncherForActivityResult() به شما امکان می‌دهد نتیجه‌ای از فعالیت در composable خود دریافت کنید:

@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() را نشان می‌دهد. با تک‌ضرب زدن روی دکمه، درخواست راه‌اندازی می‌شود. وقتی کاربر تصویری را انتخاب می‌کند و به فعالیت راه‌اندازی برمی‌گردد، لامبدای دنباله‌دار برای rememberLauncherForActivityResult() فراخوانده می‌شود. این کار تصویر انتخاب‌شده را بااستفاده از تابع rememberImagePainter() Coil بار می‌کند.

هر زیرکلاس ActivityResultContract می‌تواند به‌عنوان اولین شناسه برای rememberLauncherForActivityResult() استفاده شود. این یعنی می‌توانید از این تکنیک برای درخواست محتوا از چارچوب و در الگوهای رایج دیگر استفاده کنید. همچنین می‌توانید قراردادهای سفارشی خودتان را ایجاد کنید و از آن‌ها با این تکنیک استفاده کنید.

درحال درخواست اجازه‌های زمان اجرا

از همان Activity Result API و rememberLauncherForActivityResult() که در بالا توضیح داده شد می‌توان برای درخواست اجازه‌های زمان اجرا بااستفاده از قرارداد RequestPermission برای یک اجازه یا قرارداد RequestMultiplePermissions برای چند اجازه استفاده کرد.

کتابخانه Accompanist Permissions نیز می‌تواند به‌عنوان لایه‌ای بالای آن میاناهای برنامه‌سازی کاربردی استفاده شود تا وضعیت اعطاشده فعلی برای اجازه‌ها را به «وضعیتی» نگاشت کند که Compose UI شما بتواند از آن استفاده کند.

مدیریت دکمه برگشت سیستم

برای ارائه پیمایش به عقب سفارشی و لغو رفتار پیش‌فرض دکمه برگشتن سیستم از داخل ترکیب‌پذیرتان، ترکیب‌پذیرتان می‌تواند از BackHandler برای رهگیری آن رویداد استفاده کند:

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

آرگومان اول کنترل می‌کند که آیا BackHandler درحال‌حاضر فعال است یا نه؛ می‌توانید از این آرگومان برای غیرفعال کردن موقت گرداننده خود براساس وضعیت مؤلفه‌تان استفاده کنید. اگر کاربر رویداد برگشت سیستم را راه‌اندازی کند و BackHandler درحال‌حاضر فعال باشد، لامبدای دنباله‌دار فراخوانده می‌شود.

ViewModel

اگر از کتابخانه Architecture Components ViewModel استفاده می‌کنید، می‌توانید با فراخوانی تابع viewModel() از هر composable به ViewModel دسترسی پیدا کنید. وابستگی زیر را به فایل Gradle خود اضافه کنید:

شیک

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

کاتلین

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 برگردانده‌شده به فعالیت، قطعه، یا مقصد پیمایش دربرگیرنده محدود می‌شود و تا زمانی که محدوده فعال باشد حفظ می‌شود.

برای مثال، اگر از composable در فعالیتی استفاده شود، 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 به سایر composableها خودداری کنید، زیرا این کار می‌تواند آزمایش آن composableها را دشوارتر کند و ممکن است پیش‌نمایش‌ها را خراب کند. به‌جای آن، فقط داده‌ها و عملکردهایی را که نیاز دارند به‌عنوان پارامتر ارسال کنید.

You can use ViewModel instances to manage state for sub screen-level composables, however, be aware of the ViewModel's lifecycle and scope. اگر composable مستقل است، می‌توانید از Hilt برای تزریق ViewModel استفاده کنید تا مجبور نباشید وابستگی‌ها را از composableهای والد ارسال کنید.

اگر ViewModel شما وابستگی‌هایی داشته باشد، viewModel() یک ViewModelProvider.Factory اختیاری را به‌عنوان پارامتر می‌گیرد.

برای اطلاعات بیشتر درباره ViewModel در Compose و نحوه استفاده از نمونه‌ها با کتابخانه Navigation Compose، یا فعالیت‌ها و قطعه‌ها، اسناد هم‌کنش‌پذیری را ببینید.

جریان‌های داده‌ها

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 استفاده شده است دوباره ترکیب‌بندی می‌کند. برای مثال، در این کد، هر بار که exampleLiveData مقدار جدیدی را منتشر می‌کند، ShowData دوباره ترکیب می‌شود.

// 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 به شما امکان می‌دهد عملیات ناهمگام را بااستفاده از کوروتین‌ها از داخل composableهایتان اجرا کنید.

برای کسب اطلاعات بیشتر، میاناهای برنامه‌سازی کاربردی LaunchedEffect،‏ produceState، و rememberCoroutineScope را در اسناد عوارض جانبی ببینید.

مؤلفه پیمایش از برنامه‌های Jetpack Compose پشتیبانی می‌کند. برای اطلاعات بیشتر، پیمایش با Compose و انتقال پیمایش Jetpack به 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'
}

کاتلین

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 اضافی است که باید به پروژه و نسخه آن اضافه شود.

در اینجا مثالی از میاناهای برنامه‌سازی کاربردی 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 Maps در برنامه‌تان استفاده کنید. در اینجا نمونه‌ای از کاربرد آن آورده شده است:

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