1. مقدمه
در این کد لبه یاد خواهید گرفت که چگونه برنامههای تطبیقی برای تلفنها، تبلتها و تاشوها بسازید، و چگونه آنها قابلیت دسترسی را با Jetpack Compose افزایش میدهند. همچنین بهترین روشها را برای استفاده از مؤلفههای Material 3 و قالببندی یاد خواهید گرفت.
قبل از غواصی، مهم است که منظورمان از سازگاری را درک کنیم.
سازگاری
UI برای برنامه شما باید پاسخگو باشد تا اندازه های مختلف پنجره، جهت گیری ها و عوامل شکل را در نظر بگیرد. یک چیدمان تطبیقی بر اساس فضای صفحه نمایش در دسترس آن تغییر می کند. این تغییرات از تنظیمات ساده چیدمان برای پر کردن فضا، انتخاب سبکهای ناوبری مربوطه، تا تغییر کامل چیدمان برای استفاده از اتاق اضافی را شامل میشود.
برای کسب اطلاعات بیشتر، طراحی تطبیقی را بررسی کنید.
در این کد لبه، نحوه استفاده و تفکر در مورد سازگاری هنگام استفاده از Jetpack Compose را بررسی می کنید. شما برنامه ای به نام Reply می سازید که به شما نشان می دهد چگونه سازگاری را برای انواع صفحه نمایش ها پیاده سازی کنید، و چگونه سازگاری و دسترس پذیری با هم کار می کنند تا تجربه ای بهینه را به کاربران ارائه دهند.
چیزی که یاد خواهید گرفت
- چگونه برنامه خود را طوری طراحی کنید که تمام اندازه های پنجره را با Jetpack Compose هدف قرار دهد.
- چگونه برنامه خود را برای تاشوهای مختلف هدف قرار دهید.
- نحوه استفاده از انواع مختلف ناوبری برای دسترسی و دسترسی بهتر.
- نحوه استفاده از اجزای Material 3 برای ارائه بهترین تجربه برای هر اندازه پنجره.
آنچه شما نیاز دارید
- آخرین نسخه پایدار اندروید استودیو .
- یک دستگاه مجازی اندروید 13 با قابلیت تغییر اندازه .
- دانش کاتلین
- درک اولیه از Compose (مانند حاشیه نویسی
@Composable
). - آشنایی اولیه با طرحبندیهای Compose (مثلاً
Row
وColumn
). - آشنایی اولیه با مدیفایرها (به عنوان مثال
Modifier.padding()
).
شما از شبیه ساز Resizable برای این کد لبه استفاده خواهید کرد که به شما امکان می دهد بین انواع مختلف دستگاه ها و اندازه های پنجره جابجا شوید.
اگر با Compose آشنا نیستید، قبل از تکمیل این کد لبه کدهای اصولی Jetpack Compose را در نظر بگیرید.
چیزی که خواهی ساخت
- یک برنامه کلاینت ایمیل تعاملی به نام Reply که از بهترین شیوهها برای طراحیهای سازگار، ناوبری مواد مختلف و استفاده بهینه از فضای صفحه استفاده میکند.
2. راه اندازی شوید
برای دریافت کد این کد لبه، مخزن GitHub را از خط فرمان کلون کنید:
git clone https://github.com/android/codelab-android-compose.git cd codelab-android-compose/AdaptiveUiCodelab
همچنین، می توانید مخزن را به عنوان یک فایل ZIP دانلود کنید:
توصیه می کنیم از کدهای موجود در شعبه اصلی شروع کنید و گام به گام کد لبه را با سرعت خود دنبال کنید.
پروژه را در اندروید استودیو باز کنید
- در پنجره خوش آمدید به Android Studio ، را انتخاب کنید
یک پروژه موجود را باز کنید.
- پوشه
<Download Location>/AdaptiveUiCodelab
انتخاب کنید (مطمئن شوید که پوشهAdaptiveUiCodelab
حاویbuild.gradle
را انتخاب کرده اید). - وقتی اندروید استودیو پروژه را وارد کرد، آزمایش کنید که بتوانید شعبه
main
را اجرا کنید.
کد شروع را کاوش کنید
کد شعبه اصلی شامل بسته ui
است. شما با فایل های زیر در آن بسته کار خواهید کرد:
-
MainActivity.kt
- فعالیت نقطه ورودی جایی که برنامه خود را شروع می کنید. -
ReplyApp.kt
- شامل UI صفحه نمایش اصلی است. -
ReplyHomeViewModel.kt
- داده ها و وضعیت رابط کاربری را برای محتوای برنامه ارائه می دهد. -
ReplyListContent.kt
- حاوی اجزای سازنده برای ارائه لیست ها و صفحه نمایش جزئیات است.
اگر این برنامه را روی یک شبیهساز قابل تغییر اندازه اجرا کنید و انواع دستگاههای مختلف مانند تلفن یا تبلت را امتحان کنید، رابط کاربری به جای استفاده از فضای صفحه نمایش یا ارگونومی قابلیت دسترسی، فقط به فضای داده شده گسترش مییابد.
برای استفاده از فضای صفحه نمایش، افزایش قابلیت استفاده و بهبود تجربه کلی کاربر، آن را به روز می کنید.
3. برنامه ها را سازگار کنید
این بخش به معنای تطبیق پذیر کردن برنامه ها و چه مؤلفه هایی را که Material 3 برای آسان کردن این کار ارائه می دهد، معرفی می کند. همچنین انواع صفحهنمایشها و حالتهایی را که هدف قرار میدهید، شامل تلفنها، تبلتها، تبلتهای بزرگ و تاشوها را پوشش میدهد.
شما با بررسی اصول اندازه پنجره، وضعیت تا شدن، و انواع مختلف گزینه های ناوبری شروع خواهید کرد. سپس، می توانید از این API ها در برنامه خود استفاده کنید تا آن را تطبیق پذیرتر کنید.
اندازه های پنجره
دستگاههای Android در اشکال و اندازهها، از تلفنها گرفته تا تاشوها تا تبلتها و دستگاههای ChromeOS وجود دارند. برای پشتیبانی از حداکثر اندازه پنجره، رابط کاربری شما باید پاسخگو و سازگار باشد. برای کمک به شما در یافتن آستانه مناسب برای تغییر رابط کاربری برنامه خود، مقادیر نقطه شکست را تعریف کردهایم که به طبقهبندی دستگاهها به کلاسهای اندازه از پیش تعریفشده (کامپکت، متوسط و گسترده)، به نام کلاسهای اندازه پنجره کمک میکند. اینها مجموعهای از نقاط شکست دیدگاه نظری هستند که به شما در طراحی، توسعه و آزمایش طرحبندی برنامههای پاسخگو و تطبیقی کمک میکنند.
دسته ها به طور خاص برای متعادل کردن سادگی چیدمان، با انعطاف پذیری برای بهینه سازی برنامه شما برای موارد منحصر به فرد انتخاب شده اند. کلاس اندازه پنجره همیشه با فضای صفحه در دسترس برنامه تعیین می شود، که ممکن است کل صفحه فیزیکی برای انجام چند کار یا بخش بندی های دیگر نباشد.
هم عرض و هم ارتفاع به طور جداگانه طبقهبندی میشوند، بنابراین در هر نقطه از زمان، برنامه شما دارای دو کلاس اندازه پنجره است - یکی برای عرض و دیگری برای ارتفاع. عرض موجود معمولاً به دلیل فراگیر بودن اسکرول عمودی از ارتفاع موجود مهمتر است، بنابراین برای این مورد از کلاس های اندازه عرض نیز استفاده خواهید کرد.
حالت های فولد
دستگاههای تاشو به دلیل اندازههای متفاوت و وجود لولا، موقعیتهای بیشتری را ارائه میدهند که برنامه شما میتواند با آنها سازگار شود. لولاها می توانند بخشی از نمایشگر را مبهم کنند و آن ناحیه را برای نمایش محتوا نامناسب کنند. آنها همچنین می توانند جدا شوند، به این معنی که وقتی دستگاه باز می شود، دو نمایشگر فیزیکی جداگانه وجود دارد.
علاوه بر این، کاربر میتواند در حالی که لولا تا حدی باز است به صفحه نمایش داخلی نگاه کند، که در نتیجه وضعیتهای فیزیکی متفاوتی بر اساس جهت چین ایجاد میکند: وضعیت روی میز (تاهای افقی، در تصویر بالا به سمت راست نشان داده شده است) و وضعیت کتاب ( چین عمودی).
در مورد وضعیت های چین دار و لولا بیشتر بخوانید.
همه اینها مواردی هستند که باید هنگام اجرای طرحبندیهای تطبیقی که از تاشوها پشتیبانی میکنند، در نظر گرفت.
اطلاعات تطبیقی را دریافت کنید
کتابخانه adaptive
Material3 دسترسی راحت به اطلاعات مربوط به پنجره ای که برنامه شما در آن اجرا می شود، فراهم می کند.
- ورودی های این مصنوع و نسخه آن را به فایل کاتالوگ نسخه اضافه کنید:
gradle/libs.versions.toml
[versions]
material3Adaptive = "1.0.0"
[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
- در فایل ساخت ماژول برنامه، وابستگی کتابخانه جدید را اضافه کنید و سپس یک همگام سازی Gradle را انجام دهید:
app/build.gradle.kts
dependencies {
implementation(libs.androidx.material3.adaptive)
}
اکنون، در هر محدوده قابل ترکیب، میتوانید از currentWindowAdaptiveInfo()
برای دریافت یک شیء WindowAdaptiveInfo
حاوی اطلاعاتی مانند کلاس اندازه پنجره فعلی و اینکه آیا دستگاه در وضعیت تاشو مانند وضعیت روی میز قرار دارد استفاده کنید.
اکنون می توانید این را در MainActivity
امتحان کنید.
- در
onCreate()
در داخل بلوکReplyTheme
، اطلاعات تطبیقی پنجره را دریافت کنید و کلاسهای اندازه را در یکText
composable نمایش دهید. می توانید این را بعد از عنصرReplyApp()
اضافه کنید:
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ReplyTheme {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
ReplyApp(
replyHomeUIState = uiState,
onEmailClick = viewModel::setSelectedEmail
)
val adaptiveInfo = currentWindowAdaptiveInfo()
val sizeClassText =
"${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
"${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
Text(
text = sizeClassText,
color = Color.Magenta,
modifier = Modifier.padding(
WindowInsets.safeDrawing.asPaddingValues()
)
)
}
}
}
با اجرای برنامه اکنون کلاس های اندازه پنجره چاپ شده روی محتوای برنامه نشان داده می شود. احساس راحتی کنید که چه چیز دیگری در اطلاعات تطبیقی پنجره ارائه شده است. پس از آن می توانید این Text
حذف کنید زیرا محتوای برنامه را پوشش می دهد و برای مراحل بعدی لازم نیست.
4. ناوبری پویا
اکنون با تغییر وضعیت و اندازه دستگاه، ناوبری برنامه را تطبیق خواهید داد تا استفاده از برنامه خود را آسان تر کنید.
وقتی کاربران گوشی را در دست می گیرند، انگشتان آن ها معمولا در پایین صفحه نمایش قرار می گیرد. وقتی کاربران یک دستگاه تاشو باز شده یا تبلت را در دست می گیرند، انگشتانشان معمولاً به کناره ها نزدیک می شود. کاربران شما باید بتوانند بدون نیاز به موقعیت شدید دست یا تغییر محل قرارگیری دست خود، مسیریابی یا تعامل با یک برنامه را آغاز کنند.
همانطور که برنامه خود را طراحی می کنید و تصمیم می گیرید که عناصر رابط کاربری تعاملی را در چیدمان خود قرار دهید، مفاهیم ارگونومیک مناطق مختلف صفحه را در نظر بگیرید.
- هنگام در دست گرفتن دستگاه، دسترسی به کدام مناطق راحت است؟
- تنها با دراز کردن انگشتان می توان به کدام مناطق رسید که ممکن است ناراحت کننده باشد؟
- دسترسی به کدام مناطق چالش برانگیز است یا از جایی که کاربر دستگاه را در دست دارد دور است؟
ناوبری اولین چیزی است که کاربران با آن در تعامل هستند و شامل اقدامات بسیار مهم مربوط به سفرهای حیاتی کاربر است، بنابراین باید در مناطقی قرار گیرد که دسترسی به آن آسانتر است. کتابخانه تطبیقی Material چندین مؤلفه را فراهم می کند که بسته به کلاس اندازه پنجره دستگاه، به شما کمک می کند تا پیمایش را پیاده سازی کنید.
ناوبری پایین
ناوبری پایین برای اندازه های جمع و جور عالی است، زیرا ما به طور طبیعی دستگاه را در جایی نگه می داریم که انگشت شست ما به راحتی به تمام نقاط لمس ناوبری پایین دست یابد. هر زمان که یک دستگاه جمع و جور یا یک دستگاه تاشو در حالت جمع و جور جمع و جور دارید از آن استفاده کنید.
ریل ناوبری
برای اندازه پنجره با عرض متوسط ، ریل ناوبری برای دسترسی ایده آل است زیرا انگشت شست ما به طور طبیعی در کنار دستگاه می افتد. همچنین می توانید برای نمایش اطلاعات بیشتر، ریل ناوبری را با کشوی ناوبری ترکیب کنید.
کشوی ناوبری
کشوی پیمایش راهی آسان برای مشاهده اطلاعات دقیق برای برگههای پیمایش فراهم میکند و هنگامی که از رایانه لوحی یا دستگاههای بزرگتر استفاده میکنید به راحتی قابل دسترسی است. دو نوع کشو ناوبری موجود است: کشوی ناوبری مدال و کشوی ناوبری دائمی.
کشوی ناوبری معین
میتوانید از کشوی ناوبری مدال برای تلفنها و تبلتهای کوچک تا متوسط استفاده کنید، زیرا میتوان آن را بهعنوان پوششی روی محتوا گسترش داد یا پنهان کرد. گاهی اوقات می توان آن را با ریل ناوبری ترکیب کرد.
کشوی ناوبری دائمی
میتوانید از کشوی پیمایش دائمی برای پیمایش ثابت در رایانههای لوحی بزرگ، Chromebook و رایانههای رومیزی استفاده کنید.
پیاده سازی ناوبری پویا
اکنون، با تغییر وضعیت و اندازه دستگاه، بین انواع مختلف پیمایش جابهجا خواهید شد.
در حال حاضر، برنامه همیشه بدون توجه به وضعیت دستگاه NavigationBar
در زیر محتوای صفحه نمایش می دهد. در عوض، میتوانید از مؤلفه Material NavigationSuiteScaffold
برای جابهجایی خودکار بین مؤلفههای ناوبری مختلف بر اساس اطلاعاتی مانند کلاس اندازه پنجره فعلی استفاده کنید.
- با بهروزرسانی کاتالوگ نسخه و اسکریپت ساخت برنامه، وابستگی Gradle را برای دریافت این مؤلفه اضافه کنید، سپس همگامسازی Gradle را انجام دهید:
gradle/libs.versions.toml
[versions]
material3AdaptiveNavSuite = "1.3.0"
[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }
app/build.gradle.kts
dependencies {
implementation(libs.androidx.material3.adaptive.navigation.suite)
}
- تابع composable
ReplyNavigationWrapper()
درReplyApp.kt
پیدا کنید وColumn
و محتویات آن را باNavigationSuiteScaffold
جایگزین کنید:
ReplyApp.kt
@Composable
private fun ReplyNavigationWrapperUI(
content: @Composable () -> Unit = {}
) {
var selectedDestination: ReplyDestination by remember {
mutableStateOf(ReplyDestination.Inbox)
}
NavigationSuiteScaffold(
navigationSuiteItems = {
ReplyDestination.entries.forEach {
item(
selected = it == selectedDestination,
onClick = { /*TODO update selection*/ },
icon = {
Icon(
imageVector = it.icon,
contentDescription = stringResource(it.labelRes)
)
},
label = {
Text(text = stringResource(it.labelRes))
},
)
}
}
) {
content()
}
}
آرگومان navigationSuiteItems
بلوکی است که به شما امکان می دهد آیتم ها را با استفاده از تابع item()
اضافه کنید، شبیه به افزودن آیتم ها در LazyColumn
. در داخل لامبدا انتهایی، این کد content()
ارسال شده به عنوان آرگومان به ReplyNavigationWrapperUI()
را فراخوانی می کند.
برنامه را روی شبیهساز اجرا کنید و اندازههای گوشی، تاشو و تبلت را تغییر دهید، میبینید که نوار ناوبری به ریل ناوبری و برگشت تغییر میکند.
در پنجرههای بسیار عریض، مانند تبلت در حالت افقی، ممکن است بخواهید کشوی پیمایش دائمی را نشان دهید. NavigationSuiteScaffold
از نمایش کشوی دائمی پشتیبانی می کند، اگرچه در هیچ یک از مقادیر فعلی WindowWidthSizeClass
نشان داده نمی شود. با این حال، شما می توانید آن را با یک تغییر کوچک انجام دهید.
- کد زیر را درست قبل از تماس به
NavigationSuiteScaffold
اضافه کنید:
ReplyApp.kt
@Composable
private fun ReplyNavigationWrapperUI(
content: @Composable () -> Unit = {}
) {
var selectedDestination: ReplyDestination by remember {
mutableStateOf(ReplyDestination.Inbox)
}
val windowSize = with(LocalDensity.current) {
currentWindowSize().toSize().toDpSize()
}
val layoutType = if (windowSize.width >= 1200.dp) {
NavigationSuiteType.NavigationDrawer
} else {
NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
currentWindowAdaptiveInfo()
)
}
NavigationSuiteScaffold(
layoutType = layoutType,
...
) {
content()
}
}
این کد ابتدا اندازه پنجره را دریافت می کند و با استفاده از currentWindowSize()
و LocalDensity.current
آن را به واحدهای DP تبدیل می کند و سپس عرض پنجره را برای تصمیم گیری در مورد نوع طرح رابط کاربری ناوبری مقایسه می کند. اگر عرض پنجره حداقل 1200.dp
باشد، از NavigationSuiteType.NavigationDrawer
استفاده می کند. در غیر این صورت، به محاسبه پیش فرض برمی گردد.
وقتی دوباره برنامه را روی شبیهساز قابل تغییر اندازه خود اجرا میکنید و انواع مختلف را امتحان میکنید، توجه داشته باشید که هر زمان که پیکربندی صفحه تغییر میکند یا دستگاه تاشو را باز میکنید، مسیریابی به نوع مناسب برای آن اندازه تغییر میکند.
تبریک می گوییم، شما در مورد انواع مختلف پیمایش برای پشتیبانی از انواع مختلف اندازه ها و حالت های پنجره یاد گرفته اید!
در بخش بعدی، نحوه استفاده از هر قسمت باقیمانده صفحه را به جای کشاندن همان آیتم فهرست از لبه به لبه بررسی میکنید.
5. استفاده از فضای صفحه نمایش
مهم نیست که برنامه را روی تبلت کوچک، دستگاه باز یا تبلت بزرگ اجرا میکنید، صفحه نمایش کشیده میشود تا فضای باقیمانده را پر کند. میخواهید مطمئن شوید که میتوانید از آن فضای صفحه برای نمایش اطلاعات بیشتر استفاده کنید، مانند این برنامه، نشان دادن ایمیل و موضوعات به کاربران در همان صفحه.
متریال 3 سه طرح بندی متعارف را تعریف می کند که هر کدام دارای پیکربندی برای کلاس های اندازه پنجره فشرده، متوسط و گسترش یافته هستند. طرح بندی متعارف List Detail برای این مورد مناسب است و به صورت ListDetailPaneScaffold
در دسترس است.
- این مؤلفه را با افزودن وابستگیهای زیر و انجام همگامسازی Gradle دریافت کنید:
gradle/libs.versions.toml
[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }
app/build.gradle.kts
dependencies {
implementation(libs.androidx.material3.adaptive.layout)
implementation(libs.androidx.material3.adaptive.navigation)
}
- تابع composable
ReplyAppContent()
را درReplyApp.kt
پیدا کنید، که در حال حاضر تنها با فراخوانیReplyListPane()
صفحه لیست را نشان می دهد. با قرار دادن کد زیر، این پیاده سازی را باListDetailPaneScaffold
جایگزین کنید. از آنجایی که این یک API آزمایشی است، حاشیه نویسی@OptIn
را نیز در تابعReplyAppContent()
اضافه خواهید کرد:
ReplyApp.kt
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
replyHomeUIState: ReplyHomeUIState,
onEmailClick: (Email) -> Unit,
) {
val navigator = rememberListDetailPaneScaffoldNavigator<Long>()
ListDetailPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
listPane = {
ReplyListPane(replyHomeUIState, onEmailClick)
},
detailPane = {
ReplyDetailPane(replyHomeUIState.emails.first())
}
)
}
این کد ابتدا با استفاده از rememberListDetailPaneNavigator ()
یک ناوبر ایجاد می کندrememberListDetailPaneNavigator ()
. ناوبر مقداری کنترل بر روی اینکه کدام صفحه نمایش داده می شود و محتوایی که باید در آن صفحه نمایش داده شود، فراهم می کند که بعداً نشان داده خواهد شد.
ListDetailPaneScaffold
هنگامی که کلاس اندازه عرض پنجره گسترش می یابد، دو پنجره را نشان می دهد. در غیر این صورت، یک صفحه یا صفحه دیگر را بر اساس مقادیر ارائه شده برای دو پارامتر نشان می دهد: دستور داربست و مقدار داربست. برای دریافت رفتار پیشفرض، این کد از دستور داربست و مقدار اسکافولد ارائه شده توسط ناوبر استفاده میکند.
پارامترهای مورد نیاز باقیمانده لامبداهای قابل ترکیب برای شیشه ها هستند. ReplyListPane()
و ReplyDetailPane()
(که در ReplyListContent.kt
یافت می شود) به ترتیب برای پر کردن نقش های لیست و پنجره های جزئیات استفاده می شوند. ReplyDetailPane()
انتظار یک آرگومان ایمیل را دارد، بنابراین در حال حاضر این کد از اولین ایمیل از لیست ایمیل ها در ReplyHomeUIState
استفاده می کند.
برنامه را اجرا کنید و نمای شبیه ساز را به تاشو یا تبلت تغییر دهید (ممکن است مجبور شوید جهت را تغییر دهید) تا طرح بندی دو صفحه را ببینید. این در حال حاضر بسیار بهتر از قبل به نظر می رسد!
حال به برخی از رفتارهای مطلوب این صفحه می پردازیم. وقتی کاربر روی ایمیلی در قسمت لیست ضربه می زند، باید آن را به همراه تمام پاسخ ها در قسمت جزئیات نشان داده شود. در حال حاضر، برنامه ایمیل انتخاب شده را ردیابی نمی کند و ضربه زدن روی یک مورد هیچ کاری انجام نمی دهد. بهترین مکان برای نگهداری این اطلاعات با بقیه حالت رابط کاربری در ReplyHomeUIState
است.
-
ReplyHomeViewModel.kt
را باز کنید و کلاس دادهReplyHomeUIState
را پیدا کنید. یک ویژگی برای ایمیل انتخابی با مقدار پیشفرضnull
اضافه کنید:
ReplyHomeViewModel.kt
data class ReplyHomeUIState(
val emails : List<Email> = emptyList(),
val selectedEmail: Email? = null,
val loading: Boolean = false,
val error: String? = null
)
- در همان فایل،
ReplyHomeViewModel
یک تابعsetSelectedEmail()
دارد که وقتی کاربر روی آیتم لیست ضربه می زند، فراخوانی می شود. این تابع را برای کپی کردن وضعیت رابط کاربری و ضبط ایمیل انتخابی تغییر دهید:
ReplyHomeViewModel.kt
fun setSelectedEmail(email: Email) {
_uiState.update {
it.copy(selectedEmail = email)
}
}
چیزی که باید در نظر گرفته شود این است که قبل از اینکه کاربر روی هر موردی ضربه بزند و ایمیل انتخابی null
شود چه اتفاقی می افتد. چه چیزی باید در قسمت جزئیات نمایش داده شود؟ راه های مختلفی برای رسیدگی به این مورد وجود دارد، مانند نشان دادن اولین مورد در لیست به طور پیش فرض.
- در همان فایل، تابع
observeEmails()
تغییر دهید. وقتی لیست ایمیل ها بارگیری شد، اگر حالت رابط کاربری قبلی ایمیل انتخابی نداشت، آن را روی اولین مورد تنظیم کنید:
ReplyHomeViewModel.kt
private fun observeEmails() {
viewModelScope.launch {
emailsRepository.getAllEmails()
.catch { ex ->
_uiState.value = ReplyHomeUIState(error = ex.message)
}
.collect { emails ->
val currentSelection = _uiState.value.selectedEmail
_uiState.value = ReplyHomeUIState(
emails = emails,
selectedEmail = currentSelection ?: emails.first()
)
}
}
}
- به
ReplyApp.kt
بازگردید و از ایمیل انتخابی، در صورت موجود بودن، برای پر کردن محتوای صفحه جزئیات استفاده کنید:
ReplyApp.kt
ListDetailPaneScaffold(
// ...
detailPane = {
if (replyHomeUIState.selectedEmail != null) {
ReplyDetailPane(replyHomeUIState.selectedEmail)
}
}
)
برنامه را دوباره اجرا کنید و شبیه ساز را به اندازه تبلت تغییر دهید و ببینید که با ضربه زدن روی یک آیتم لیست، محتویات قسمت جزئیات به روز می شود.
زمانی که هر دو صفحه قابل مشاهده باشند، این کار عالی عمل می کند، اما زمانی که پنجره تنها فضایی برای نشان دادن یک صفحه دارد، به نظر می رسد با ضربه زدن روی یک مورد هیچ اتفاقی نمی افتد. سعی کنید نمای شبیه ساز را به یک تلفن یا یک دستگاه تاشو به صورت عمودی تغییر دهید و متوجه شوید که حتی پس از ضربه زدن روی یک مورد، فقط صفحه فهرست قابل مشاهده است. دلیلش این است که حتی اگر ایمیل انتخابی بهروزرسانی میشود، ListDetailPaneScaffold
تمرکز خود را بر روی صفحه فهرست در این پیکربندیها حفظ میکند.
- برای رفع آن، کد زیر را به عنوان لامبدا به
ReplyListPane
وارد کنید:
ReplyApp.kt
ListDetailPaneScaffold(
// ...
listPane = {
ReplyListPane(
replyHomeUIState = replyHomeUIState,
onEmailClick = { email ->
onEmailClick(email)
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
}
)
},
// ...
)
این لامبدا از ناوبری که قبلاً ایجاد شده است استفاده می کند تا وقتی روی یک مورد کلیک می شود رفتار اضافی اضافه کند. لامبدای اصلی ارسال شده به این تابع را فراخوانی میکند و سپس navigator.navigateTo()
را نیز فراخوانی میکند و مشخص میکند کدام صفحه باید نشان داده شود. هر پنجره در داربست دارای یک نقش مرتبط با خود است و برای قسمت جزئیات، ListDetailPaneScaffoldRole.Detail
است. در پنجرههای کوچکتر، این ظاهری را نشان میدهد که برنامه به جلو حرکت کرده است.
این برنامه همچنین باید کارهایی را انجام دهد که کاربر دکمه برگشت را از قسمت جزئیات فشار میدهد و این رفتار بسته به اینکه یک صفحه یا دو صفحه قابل مشاهده باشد متفاوت خواهد بود.
- با افزودن کد زیر از ناوبری برگشت پشتیبانی کنید.
ReplyApp.kt
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
replyHomeUIState: ReplyHomeUIState,
onEmailClick: (Email) -> Unit,
) {
val navigator = rememberListDetailPaneScaffoldNavigator<Long>()
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
}
ListDetailPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
listPane = {
AnimatedPane {
ReplyListPane(
replyHomeUIState = replyHomeUIState,
onEmailClick = { email ->
onEmailClick(email)
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
}
)
}
},
detailPane = {
AnimatedPane {
if (replyHomeUIState.selectedEmail != null) {
ReplyDetailPane(replyHomeUIState.selectedEmail)
}
}
}
)
}
ناوبر وضعیت کامل ListDetailPaneScaffold
را می داند، آیا پیمایش برگشتی امکان پذیر است و در همه این سناریوها چه کاری باید انجام دهد. این کد یک BackHandler
ایجاد می کند که هر زمان که ناوبر بتواند به عقب برگردد فعال می شود و در داخل لامبدا navigateBack()
فرا می خواند. همچنین، برای اینکه انتقال بین پنجرهها بسیار روانتر شود، هر صفحه در یک AnimatedPane()
پیچیده میشود.
برنامه را دوباره بر روی یک شبیه ساز قابل تغییر اندازه برای انواع مختلف دستگاه ها اجرا کنید و متوجه شوید که هر زمان که پیکربندی صفحه تغییر می کند یا دستگاه تاشو را باز می کنید، ناوبری و محتوای صفحه به صورت پویا در پاسخ به تغییر وضعیت دستگاه تغییر می کند. همچنین سعی کنید روی ایمیلها در صفحه فهرست ضربه بزنید و ببینید که چیدمان در صفحههای مختلف چگونه رفتار میکند، هر دو صفحه را در کنار هم نشان میدهد یا بین آنها به آرامی متحرک میشود.
تبریک میگوییم، شما با موفقیت برنامه خود را با انواع حالتها و اندازههای دستگاه سازگار کردهاید. ادامه دهید و با اجرای برنامه در تاشوها، تبلت ها یا سایر دستگاه های تلفن همراه بازی کنید.
6. تبریک می گویم
تبریک می گویم! شما با موفقیت این کد را تکمیل کردید و یاد گرفتید که چگونه برنامهها را با Jetpack Compose سازگار کنید.
یاد گرفتید که چگونه اندازه دستگاه و وضعیت تاشو را بررسی کنید و بر اساس آن رابط کاربری، پیمایش و سایر عملکردهای برنامه خود را به روز کنید. همچنین یاد گرفتید که چگونه سازگاری دسترسی را بهبود می بخشد و تجربه کاربر را افزایش می دهد.
بعدش چی؟
سایر کدها را در مسیر Compose بررسی کنید.
نمونه برنامه ها
- نمونههای نوشتن مجموعهای از بسیاری از برنامهها هستند که بهترین شیوههای توضیح داده شده در لبههای کد را در خود جای دادهاند.