1. Giriş
Bu codelab'de telefonlar, tabletler ve katlanabilir cihazlar için uyarlanabilir uygulamalar geliştirmeyi ve bu uygulamaların Jetpack Compose ile erişilebilirliği nasıl artırdığını öğreneceksiniz. Ayrıca, Materyal 3 bileşenlerini ve temalarını kullanmayla ilgili en iyi uygulamaları öğreneceksiniz.
Başlamadan önce uyarlanabilirlik ile neyi kastettiğimizi anlamak gerekiyor.
Uyarlanabilirlik
Uygulamanızın kullanıcı arayüzü, farklı pencere boyutlarını, yönleri ve form faktörlerini hesaba katarak duyarlı olmalıdır. Uyarlanabilir düzen, kullanılabilen ekran alanına bağlı olarak değişir. Bu değişiklikler arasında, basit düzen düzenlemelerinden alan doldurmaya, ilgili gezinme stillerini seçmeye ve ek alandan yararlanmak için düzenleri tamamen değiştirmeye kadar pek çok değişiklik bulunuyor.
Daha fazla bilgi edinmek için Uyarlanabilir tasarım sayfasına göz atın.
Bu codelab'de, Jetpack Compose'u kullanırken uyarlanabilirliği nasıl kullanacağınızı ve üzerinde düşünmeyi öğreneceksiniz. Her tür ekrana uyarlanabilme özelliğini nasıl uygulayacağınızı ve kullanıcılara en iyi deneyimi sunmak için uyarlanabilirlik ile erişilebilirliğin birlikte nasıl çalıştığını gösteren Yanıtla adlı bir uygulama geliştiriyorsunuz.
Neler öğreneceksiniz?
- Jetpack Compose ile uygulamanızı tüm pencere boyutlarını hedefleyecek şekilde tasarlama.
- Uygulamanızı farklı katlanabilir cihazlar için hedefleme.
- Daha iyi erişilebilirlik ve erişilebilirlik için farklı gezinme türlerinin nasıl kullanılacağı
- Her pencere boyutunda en iyi deneyimi sağlamak için Materyal 3 bileşenleri nasıl kullanılır?
Gerekenler
- Android Studio'nun en son kararlı sürümü.
- Android 13'te yeniden boyutlandırılabilen bir sanal cihaz.
- Kotlin bilgisi.
- Oluşturma ile ilgili temel bilgileri (
@Composable
ek açıklaması gibi). - Oluşturma düzenleriyle ilgili temel düzeyde bilgi (ör.
Row
veColumn
). - Değiştiricilere aşinalık (ör.
Modifier.padding()
).
Bu codelab için farklı cihaz türleri ve pencere boyutları arasında geçiş yapmanıza olanak tanıyan Yeniden boyutlandırılabilir emülatörü kullanacaksınız.
Compose'a aşina değilseniz bu codelab'i tamamlamadan önce Jetpack Compose temel kod laboratuvarı'na katılabilirsiniz.
Neler oluşturacaksınız?
- Uyarlanabilir tasarımlar, farklı Material gezinme seçenekleri ve optimum ekran alanı kullanımı için en iyi uygulamalardan yararlanan Reply adlı etkileşimli e-posta istemci uygulaması.
2. Hazırlanın
Bu codelab'in kodunu almak için GitHub deposunu komut satırından klonlayın:
git clone https://github.com/android/codelab-android-compose.git cd codelab-android-compose/AdaptiveUiCodelab
Alternatif olarak, depoyu ZIP dosyası olarak indirebilirsiniz:
Ana daldaki kodla başlamanızı ve codelab'i kendi hızınızda adım adım uygulamanızı öneririz.
Projeyi Android Studio'da açma
- Android Studio'ya Hoş Geldiniz penceresinde
Mevcut Bir Projeyi Aç'ı seçin.
<Download Location>/AdaptiveUiCodelab
klasörünü seçin (build.gradle
etiketini içerenAdaptiveUiCodelab
dizinini seçtiğinizden emin olun).- Android Studio projeyi içe aktardığında,
main
dalını çalıştırıp çalıştıramadığınızı test edin.
Başlangıç kodunu inceleyin
Ana dal kodu ui
paketini içerir. Bu pakette bulunan aşağıdaki dosyalarla çalışacaksınız:
MainActivity.kt
- Uygulamanızı başlattığınız giriş noktası etkinliği.ReplyApp.kt
- Ana ekran kullanıcı arayüzü composable'larını içerir.ReplyHomeViewModel.kt
- Uygulama içeriğinin verilerini ve kullanıcı arayüzü durumunu sağlar.ReplyListContent.kt
- Liste ve ayrıntı ekranları sağlamak için composable'lar içerir.
Bu uygulamayı yeniden boyutlandırılabilir bir emülatörde çalıştırıp telefon ya da tablet gibi farklı cihaz türlerini denerseniz kullanıcı arayüzü, ekran alanından yararlanmak veya erişilebilirlik ergonomisi sağlamak yerine belirli bir alana genişler.
Ekran alanından yararlanmak, kullanılabilirliği artırmak ve genel kullanıcı deneyimini iyileştirmek için sayfayı güncellersiniz.
3. Uygulamaları uyarlanabilir hale getirme
Bu bölümde, uygulamaları uyarlanabilir hale getirmenin ne anlama geldiği ve Materyal 3'ün bunu kolaylaştırmak için sağladığı bileşenler açıklanmaktadır. Ayrıca telefonlar, tabletler, büyük tabletler ve katlanabilir cihazlar da dahil olmak üzere hedefleyeceğiniz ekran ve eyalet türlerini de kapsar.
Pencere boyutları, katlama duruşu ve farklı gezinme seçenekleri ile ilgili temel bilgilerin üzerinden geçeceksiniz. Ardından, uygulamanızı daha uyarlanabilir hale getirmek için bu API'leri kullanabilirsiniz.
Pencere boyutları
Telefon, katlanabilir cihaz, tablet ve ChromeOS cihaz gibi çeşitli şekil ve boyutlarda Android cihazlar mevcuttur. Mümkün olduğunca fazla pencere boyutunu desteklemek için kullanıcı arayüzünüzün duyarlı ve uyarlanabilir olması gerekir. Uygulamanızın kullanıcı arayüzünü değiştirmek için doğru eşiği bulmanıza yardımcı olmak amacıyla, cihazların önceden tanımlanmış boyut sınıflarına (kompakt, orta ve genişletilmiş) göre sınıflandırılmasına yardımcı olan ayrılma noktası değerleri (pencere boyutu sınıfları) belirledik. Bunlar, duyarlı ve uyarlanabilir uygulama düzenlerini tasarlamanıza, geliştirmenize ve test etmenize yardımcı olan, fikir aşamasındaki görüntü alanı ayrılma noktalarından oluşur.
Kategoriler, özellikle düzen sadeliğini ve uygulamanızı benzersiz durumlara göre optimize etme esnekliğini dengelemek için seçildi. Pencere boyutu sınıfı her zaman uygulamanın kullanabildiği ekran alanına göre belirlenir. Bu alan, çoklu görev veya diğer segmentasyonlar için tam fiziksel ekranın tamamı olmayabilir.
Hem genişlik hem de yükseklik ayrı ayrı sınıflandırıldığından, herhangi bir zamanda uygulamanızda biri genişlik, diğeri yükseklik için olmak üzere iki pencere boyutu sınıfı vardır. Dikey kaydırmanın yaygın olarak kullanılması nedeniyle, kullanılabilir genişlik genellikle mevcut yükseklikten daha önemlidir. Dolayısıyla bu durumda, genişlik boyutu sınıflarını da kullanacaksınız.
Katlama durumları
Katlanabilir cihazlar, farklı boyutları ve menteşeleri sayesinde uygulamanızın uyum sağlayabileceği daha fazla durum sunar. Menteşeler ekranın bir kısmını gizleyerek içeriğin gösterilmesine engel olabilir. Bunlar da birbirinden ayrılabilir. Yani, cihaz açıkken iki ayrı fiziksel ekran olur.
Ayrıca kullanıcı, menteşe kısmen açıkken iç ekrana bakıyor olabilir. Bu durumda, katlanmanın yönüne bağlı olarak farklı fiziksel duruşlar ortaya çıkabilir: masanın üstü duruşu (yukarıdaki resimde sağda gösterilmektedir) ve kitap duru (dikey katlama).
Katlama duruşu ve menteşe hakkında daha fazla bilgi edinin.
Katlanabilir cihazları destekleyen uyarlanabilir düzenler uygularken tüm bunları göz önünde bulundurmanız gerekir.
Uyarlanabilir bilgileri alma
Malzeme3 adaptive
kitaplığı, uygulamanızın çalıştığı pencereyle ilgili bilgilere kolay erişim sağlar.
- Bu yapı ve sürümüyle ilgili girişleri sürüm katalog dosyasına ekleyin:
gradle/libs.versions.toml
[versions]
material3Adaptive = "1.0.0-beta01"
[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
- Uygulama modülünün derleme dosyasına yeni kitaplık bağımlılığını ekleyin ve ardından bir Gradle senkronizasyonu gerçekleştirin:
app/build.gradle.kts
dependencies {
implementation(libs.androidx.material3.adaptive)
}
Artık herhangi bir composable kapsamında, mevcut pencere boyutu sınıfı ve cihazın masa üstü duruşu gibi katlanabilir bir duruşta olup olmadığı gibi bilgileri içeren WindowAdaptiveInfo
nesnesi almak için currentWindowAdaptiveInfo()
aracını kullanabilirsiniz.
Bunu şimdi MainActivity
uygulamasında deneyebilirsiniz.
ReplyTheme
blokundakionCreate()
içinde pencere uyarlanabilir bilgilerini alın ve boyut sınıflarını birText
composable'da görüntüleyin. Bunu,ReplyApp()
öğesinden sonra ekleyebilirsiniz:
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(20.dp)
)
}
}
}
Uygulamayı şimdi çalıştırdığınızda uygulama içeriğinin üzerinde yazdırılan pencere boyutu sınıfları gösterilir. Pencere uyarlanabilir bilgilerinde sağlanan diğer bilgileri inceleyebilirsiniz. Daha sonra, uygulama içeriğini kapsadığı ve sonraki adımlar için gerekmeyeceği için bu Text
öğesini kaldırabilirsiniz.
4. Dinamik gezinme
Artık uygulamanızın kullanımını kolaylaştırmak için cihaz durumu ve boyutu değiştikçe uygulamanın gezinme deneyimini uyarlayacaksınız.
Kullanıcılar telefonu tutarken parmakları genellikle ekranın alt kısmındadır. Kullanıcılar açık bir katlanabilir cihazı veya tableti tutarken parmakları genellikle yanlara yakındır. Kullanıcılarınız uçtaki el konumları gerekmeden veya el yerleşimlerini değiştirmeden bir uygulamada gezinebilmeli ya da uygulamayla etkileşim başlatabilmelidir.
Uygulamanızı tasarlarken ve etkileşimli kullanıcı arayüzü öğelerinin yerleşiminizde nereye yerleştirileceğine karar verirken ekranın farklı bölgelerinin ergonomik etkilerini göz önünde bulundurun.
- Cihaz tutulduğunda rahatça ulaşılabilen bölgeler hangileri?
- Hangi alanlara yalnızca parmaklarınızı uzatarak erişilebilir ve bu durum sizin için uygun olmayabilir?
- Hangi alanlara ulaşılması zor ya da kullanıcının cihazı tuttuğu yerin çok uzakta olduğunu biliyor musunuz?
Navigasyon, kullanıcıların ilk etkileşimde bulunduğu öğedir ve kritik kullanıcı yolculuklarıyla ilgili yüksek öneme sahip işlemler içerir. Bu nedenle, gezinmenin en kolay olduğu alanlara yerleştirilmelidir. Malzeme uyarlanabilir kitaplığı, cihazın pencere boyutu sınıfına bağlı olarak gezinmeyi uygulamanıza yardımcı olan çeşitli bileşenler sağlar.
Alt gezinme
Cihazı doğal olarak baş parmağımızın alttaki tüm gezinme temas noktalarına kolayca ulaşabileceği bir yerde tutuyoruz. Bu nedenle, alt gezinme bölümü en küçük boyutlar için idealdir. Küçük boyutlu bir cihazınız veya kompakt bir şekilde katlanmış durumda olan bir katlanabilir cihazınız varsa bu uygulamayı kullanabilirsiniz.
Gezinme sütunu
Baş parmağımız doğal olarak cihazın yan tarafına düştüğünden, gezinme çubuğu orta genişlikli bir pencere boyutu için erişilebilirlik açısından ideal bir seçenektir. Daha fazla bilgi göstermek için gezinme çubuğunu bir gezinme çekmecesiyle de birleştirebilirsiniz.
Gezinme çekmecesi
Gezinme çekmecesi, gezinme sekmeleriyle ilgili ayrıntılı bilgileri görmenin kolay bir yolunu sunar ve tabletleri veya daha büyük cihazları kullandığınızda kolayca erişilebilir. İki tür gezinme çekmecesi vardır: kalıcı gezinme çekmecesi ve kalıcı gezinme çekmecesi.
Kalıcı gezinme çekmecesi
Genişletilebildiği veya gizlenebileceği için küçük ya da orta büyüklükteki telefonlar ve tabletler için kalıcı gezinme çekmecesi kullanabilirsiniz. Bu bazen bir gezinme çubuğu ile birleştirilebilir.
Kalıcı gezinme çekmecesi
Büyük tabletlerde, Chromebook'larda ve masaüstü bilgisayarlarda sabit gezinme için kalıcı bir gezinme çekmecesi kullanabilirsiniz.
Dinamik gezinme uygulama
Artık cihaz durumu ve boyutu değiştikçe farklı gezinme türleri arasında geçiş yapabilirsiniz.
Şu anda uygulama, cihaz durumundan bağımsız olarak ekran içeriğinin altında her zaman bir NavigationBar
gösteriyor. Bunun yerine, geçerli pencere boyutu sınıfı gibi bilgilere dayanarak farklı gezinme bileşenleri arasında otomatik olarak geçiş yapmak için Malzeme NavigationSuiteScaffold
bileşenini kullanabilirsiniz.
- Sürüm kataloğunu ve uygulamanın derleme komut dosyasını güncelleyerek bu bileşeni almak için Gradle bağımlılığını ekleyin, ardından bir Gradle senkronizasyonu gerçekleştirin:
gradle/libs.versions.toml
[versions]
material3AdaptiveNavSuite = "1.3.0-beta01"
[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)
}
ReplyApp.kt
içindeReplyNavigationWrapper()
composable işlevini bulupColumn
öğesini ve içeriğiniNavigationSuiteScaffold
ile değiştirin:
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
bağımsız değişkeni, LazyColumn
içindeki öğeleri eklemeye benzer şekilde, item()
işlevini kullanarak öğe eklemenizi sağlayan bir bloktur. İzleyen lambda içinde, bu kod ReplyNavigationWrapperUI()
öğesine bağımsız değişken olarak iletilen content()
öğesini çağırır.
Uygulamayı emülatörde çalıştırıp telefon, katlanabilir cihaz ve tablet arasındaki boyutları değiştirmeyi deneyin. Gezinme çubuğunun bir gezinme çubuğuna ve arkaya doğru değiştiğini görürsünüz.
Çok geniş pencerelerde (ör. yatay yönde bir tablette) kalıcı gezinme çekmecesini göstermek isteyebilirsiniz. NavigationSuiteScaffold
, geçerli WindowWidthSizeClass
değerlerinin hiçbirinde gösterilmeyen, kalıcı bir çekmece gösterilmesini destekler. Ancak küçük bir değişiklikle bunu başarabilirsiniz.
- Aşağıdaki kodu,
NavigationSuiteScaffold
çağrısından hemen önce ekleyin:
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()
}
}
Bu kod önce pencere boyutunu alır, currentWindowSize()
ve LocalDensity.current
kullanarak bunu DP birimlerine dönüştürür, ardından da gezinme kullanıcı arayüzünün düzen türüne karar vermek için pencere genişliğini karşılaştırır. Pencere genişliği en az 1200.dp
ise NavigationSuiteType.NavigationDrawer
kullanılır. Aksi takdirde varsayılan hesaplamaya geri döner.
Uygulamayı yeniden boyutlandırılabilir emülatörünüzde tekrar çalıştırıp farklı türleri denediğinizde ekran yapılandırması her değiştiğinde veya katlanabilir bir cihazı açtığınızda gezinmenin bu boyuta uygun türe değişeceğine dikkat edin.
Tebrikler, farklı pencere boyutu ve durumu türlerini desteklemek üzere farklı gezinme türleri hakkında bilgi edindiniz.
Sonraki bölümde, aynı liste öğesini kenardan kenara uzatmak yerine kalan ekran alanından nasıl yararlanacağınızı keşfedeceksiniz.
5. Ekran alanı kullanımı
Uygulamayı küçük bir tablette, açılmış bir cihazda veya büyük bir tablette çalıştırmanız fark etmeksizin ekran, kalan alanı kaplayacak şekilde genişletilir. Daha fazla bilgi (ör. bu uygulama hakkında) göstermek, kullanıcılara e-postaları ve ileti dizilerini aynı sayfada göstermek için bu ekran alanından yararlanabildiğinizden emin olmak istiyorsunuz.
Materyal 3, her biri kompakt, orta ve genişletilmiş pencere boyutu sınıflarına yönelik yapılandırmaları olan üç standart düzen tanımlar. Liste Ayrıntısı standart düzeni bu kullanım alanı için idealdir ve ListDetailPaneScaffold
olarak oluşturulabilir.
- Aşağıdaki bağımlılıkları ekleyip Gradle senkronizasyonu gerçekleştirerek bu bileşeni edinin:
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)
}
- Şu anda yalnızca
ReplyListPane()
öğesini çağırarak liste bölmesini gösterenReplyApp.kt
öğesindeReplyAppContent()
composable işlevini bulun. Aşağıdaki kodu ekleyerek bu uygulamayıListDetailPaneScaffold
ile değiştirin. Bu deneysel bir API olduğundan,ReplyAppContent()
işlevine@OptIn
ek açıklamasını da eklemeniz gerekir:
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())
}
)
}
Bu kod, önce rememberListDetailPaneNavigator
kullanarak bir gezgin oluşturur. Gezgin, hangi bölmenin görüntüleneceği ve bu bölmede hangi içeriğin temsil edilmesi gerektiği konusunda bazı kontroller sağlar; bu konu daha sonra gösterilecektir.
Pencere genişlik boyutu sınıfı genişletildiğinde ListDetailPaneScaffold
, iki bölme gösterir. Aksi takdirde, iki parametre için sağlanan değerlere göre bir bölme veya diğer bölme gösterilir: scaffold yönergesi ve scaffold değeri. Bu kod, varsayılan davranışı almak için scaffold yönergesini ve gezgin tarafından sağlanan scaffold değerini kullanır.
Geriye kalan gerekli parametreler, bölmeler için composable lambda'lardır. ReplyListPane()
ve ReplyDetailPane()
(ReplyListContent.kt
içinde bulunur), sırasıyla liste ve ayrıntı bölmelerinin rollerini doldurmak için kullanılır. ReplyDetailPane()
bir e-posta bağımsız değişkeni beklediğinden, bu kod şimdilik ReplyHomeUIState
e-posta listesindeki ilk e-postayı kullanıyor.
Uygulamayı çalıştırın ve iki bölme düzenini görmek için emülatör görünümünü katlanabilir cihaz veya tablet olarak değiştirin (yönü de değiştirmeniz gerekebilir). Bu şimdiden çok daha iyi görünüyor.
Şimdi bu ekranda istenen davranışlardan bazılarını ele alalım. Kullanıcı liste bölmesinde bir e-postaya dokunduğunda, bu ileti tüm yanıtlarla birlikte ayrıntı bölmesinde gösterilmelidir. Şu an için uygulama hangi e-postanın seçili olduğunu takip etmez ve bir öğeye dokunmanın herhangi bir etkisi olmaz. Bu bilgileri saklamak için en iyi yer, kullanıcı arayüzü durumunun geri kalanı ReplyHomeUIState
şeklindedir.
ReplyHomeViewModel.kt
sayfasını açın veReplyHomeUIState
veri sınıfını bulun. Seçili e-posta için varsayılan değerinull
olan bir mülk ekleyin:
ReplyHomeViewModel.kt
data class ReplyHomeUIState(
val emails : List<Email> = emptyList(),
val selectedEmail: Email? = null,
val loading: Boolean = false,
val error: String? = null
)
- Aynı dosyada
ReplyHomeViewModel
, kullanıcı bir liste öğesine dokunduğunda çağrılan birsetSelectedEmail()
işlevine sahiptir. Kullanıcı arayüzü durumunu kopyalamak ve seçili e-postayı kaydetmek için bu işlevi değiştirin:
ReplyHomeViewModel.kt
fun setSelectedEmail(email: Email) {
_uiState.update {
it.copy(selectedEmail = email)
}
}
Göz önünde bulundurulması gereken bir nokta, kullanıcı herhangi bir öğeye dokunmadan ve seçilen e-posta null
ise ne olduğudur. Ayrıntı bölmesinde ne görüntülenmeli? Bu durumu ele almanın birden fazla yolu vardır. Örneğin, listedeki ilk öğeyi varsayılan olarak gösterebilirsiniz.
- Aynı dosyada,
observeEmails()
işlevini değiştirin. E-posta listesi yüklendiğinde, önceki kullanıcı arayüzü durumunda seçilmiş bir e-posta yoksa bunu ilk öğeye ayarlayın:
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
sayfasına dönün ve ayrıntı bölmesi içeriğini doldurmak için seçili e-postayı (varsa) kullanın:
ReplyApp.kt
ListDetailPaneScaffold(
// ...
detailPane = {
if (replyHomeUIState.selectedEmail != null) {
ReplyDetailPane(replyHomeUIState.selectedEmail)
}
}
)
Uygulamayı tekrar çalıştırın ve emülatörü tablet boyutuna geçirin. Bir liste öğesine dokunduğunuzda ayrıntı bölmesinin içeriğinin güncellendiğini göreceksiniz.
Her iki bölme de görünür olduğunda bu iyi bir çalışmadır, ancak pencerede yalnızca bir bölmeyi göstermek için yer varsa bir öğeye dokunduğunuzda hiçbir şey olmamış gibi görünür. Emülatör görünümünü telefona veya dikey modda katlanabilir cihaza geçirmeyi deneyin. Bir öğeye dokunduktan sonra bile yalnızca liste bölmesinin görünür olduğuna dikkat edin. Bunun nedeni, seçilen e-posta güncellense bile ListDetailPaneScaffold
öğesinin, bu yapılandırmalardaki liste bölmesine odaklanmasıdır.
- Bu sorunu düzeltmek için
ReplyListPane
öğesine iletilen lambda ile aşağıdaki kodu ekleyin:
ReplyApp.kt
ListDetailPaneScaffold(
// ...
listPane = {
ReplyListPane(
replyHomeUIState = replyHomeUIState,
onEmailClick = { email ->
onEmailClick(email)
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
}
)
},
// ...
)
Bu lambda, bir öğe tıklandığında ek davranış eklemek için daha önce oluşturulan navigasyonu kullanır. Bu işleve iletilen orijinal lambda'yı çağırır ve ardından, hangi bölmenin gösterilmesi gerektiğini belirten navigator.navigateTo()
öğesini çağırır. Yapı iskelesindeki her bölmenin kendisiyle ilişkilendirilmiş bir rolü vardır ve ayrıntı bölmesi için bu rol ListDetailPaneScaffoldRole.Detail
ile belirlenir. Daha küçük pencerelerde bu, uygulamanın ileriye dönük olduğunu gösterir.
Uygulamanın, kullanıcı ayrıntı bölmesinden geri düğmesine bastığında ne olacağını da işlemesi gerekir. Bu davranış, bir veya iki bölmenin görünür olmasına bağlı olarak değişiklik gösterir.
- Aşağıdaki kodu ekleyerek geri gitmeyi destekleyin.
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)
}
}
}
)
}
Gezgin, ListDetailPaneScaffold
cihazının tüm durumunu, geri gitmenin mümkün olup olmadığını ve tüm bu senaryolarda ne yapılacağını bilir. Bu kod, gezgin geri gidebildiğinde ve lambda navigateBack()
çağrısı içinde etkinleşen bir BackHandler
oluşturur. Ayrıca, bölmeler arasındaki geçişi çok daha sorunsuz hale getirmek için her bölme bir AnimatedPane()
composable içine sarmalanmıştır.
Uygulamayı tüm farklı cihaz türleri için yeniden boyutlandırılabilen bir emülatörde tekrar çalıştırın. Ekran yapılandırması değiştiğinde veya katlanabilir bir cihazı açtığınızda gezinme ve ekran içeriğinin cihaz durumundaki değişikliklere göre dinamik olarak değiştiğine dikkat edin. Ayrıca, liste bölmesinde e-postalara hafifçe vurmayı deneyin ve düzenin farklı ekranlarda nasıl davrandığını, her iki bölmeyi yan yana gösterin veya aralarında yumuşak bir animasyon uygulayın.
Tebrikler, uygulamanızı her türlü cihaz durumuna ve boyuta başarıyla uyarlanabilir hale getirdiniz. Uygulamayı katlanabilir cihazlarda, tabletlerde veya diğer mobil cihazlarda çalıştırarak devam edin.
6. Tebrikler
Tebrikler! Bu codelab'i başarıyla tamamladınız ve Jetpack Compose ile uygulamaları uyarlanabilir hale getirmeyi öğrendiniz.
Cihazın boyutunu ve katlama durumunu kontrol etmeyi, ayrıca uygulamanızın kullanıcı arayüzünü, gezinmeyi ve diğer işlevleri uygun şekilde güncellemeyi öğrendiniz. Ayrıca uyarlanabilirliğin erişilebilirliği nasıl iyileştirdiğini ve kullanıcı deneyimini nasıl iyileştirdiğini öğrendiniz.
Sırada ne var?
Oluşturma yolu üzerindeki diğer codelab'lere göz atın.
Örnek uygulamalar
- Oluşturma örnekleri, codelab'lerde açıklanan en iyi uygulamaları içeren birçok uygulamadan oluşan bir koleksiyondur.