Jetpack Compose ile uyarlanabilir uygulamalar geliştirme

1. Giriş

Bu codelab'de telefonlar, tabletler ve katlanabilir cihazlar için nasıl uyarlanabilir uygulamalar oluşturacağınızı ve Jetpack Compose ile erişilebilirliği nasıl artıracağınızı öğreneceksiniz. Ayrıca Material 3 bileşenlerini ve temaları kullanmayla ilgili en iyi uygulamaları da öğreneceksiniz.

Konuyu incelemeye başlamadan önce, uyumluluktan neyi kastettiğimizi anlamanız önemlidir.

Uyumluluk

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, alanı doldurmak için basit düzen ayarlarından, ilgili gezinme stillerinin seçilmesine ve ek alanı kullanmak için düzenlerin tamamen değiştirilmesine kadar çeşitlilik gösteriyor.

Daha fazla bilgi edinmek için Uyarlanabilir tasarım sayfasına göz atın.

Bu codelab'de, Jetpack Compose'u kullanırken uyumluluğu nasıl kullanacağınızı ve bu konuyu nasıl ele alacağınızı keşfedeceksiniz. 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 cihazlara hedefleme.
  • Daha iyi erişim ve erişilebilirlik için farklı gezinme türlerini kullanma
  • Her pencere boyutunda en iyi deneyimi sunmak için Material 3 bileşenlerini kullanma.

İhtiyacınız olanlar

Bu codelab için farklı cihaz türleri ve pencere boyutları arasında geçiş yapmanıza olanak tanıyan Boyutlandırılabilir emülatör'ü kullanacaksınız.

Telefon, katlanmış, tablet ve masaüstü seçenekleri sunan, boyutu değiştirilebilir emülatör.

Compose'a aşina değilseniz bu kod laboratuvarını tamamlamadan önce Jetpack Compose temelleri kod laboratuvarını tamamlayabilirsiniz.

Ne oluşturacaksınız?

  • Uyarlanabilir tasarımlar, farklı Material gezinme öğeleri ve optimum ekran alanı kullanımıyla ilgili en iyi uygulamaları kullanan Reply adlı etkileşimli bir e-posta istemcisi uygulaması.

Bu kod laboratuvarında ulaşacağınız çoklu cihaz desteği gösterimi

2. Hazırlanın

Bu codelab'in kodunu almak için GitHub deposunu komut satırından kopyalayın:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

Alternatif olarak, deposu ZIP dosyası olarak da indirebilirsiniz:

main dalındaki kodla başlamanızı ve codelab'i kendi hızınıza göre adım adım uygulamanızı öneririz.

Projeyi Android Studio'da açma

  1. Android Studio'ya Hoş Geldiniz penceresinde c01826594f360d94.pngMevcut Bir Projeyi Aç'ı seçin.
  2. <Download Location>/AdaptiveUiCodelab klasörünü seçin (build.gradle etiketini içeren AdaptiveUiCodelab dizinini seçtiğinizden emin olun).
  3. Android Studio projeyi içe aktardığında main dalını çalıştırıp çalıştıramadığınızı test edin.

Başlangıç kodunu keşfetme

Ana dal kodu ui paketini içerir. Bu pakette 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ü bileşenlerini içerir.
  • ReplyHomeViewModel.kt: Uygulama içeriği için verileri ve kullanıcı arayüzü durumunu sağlar.
  • ReplyListContent.kt - Liste ve ayrıntı ekranı sağlamak için composable'lar içerir.

Bu uygulamayı yeniden boyutlandırılabilir bir emülatörde çalıştırır ve telefon veya tablet gibi farklı cihaz türlerini denerseniz kullanıcı arayüzü, ekran alanından yararlanmak veya erişilebilirlik ergonomisi sağlamak yerine yalnızca belirli bir alana genişler.

Telefonda ilk ekran

Tablette ilk genişletilmiş görünüm

Ekran alanından yararlanmak, kullanılabilirliği artırmak ve genel kullanıcı deneyimini iyileştirmek için bu sayfayı güncelleyebilirsiniz.

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. Telefonlar, tabletler, büyük tabletler ve katlanabilir ekranlar da dahil olmak üzere hedefleyeceğiniz ekran türlerini ve eyaletleri de kapsar.

Pencere boyutları, katlama duruşları ve farklı gezinme seçeneklerinin temellerini inceleyerek başlarsınız. Ardından, uygulamanızı daha uyumlu hale getirmek için bu API'leri uygulamanızda kullanabilirsiniz.

Pencere boyutları

Android cihazlar telefonlardan katlanabilir cihazlara, tabletlere ve ChromeOS cihazlara kadar her türlü şekil ve boyuttadır. Mümkün olduğunca çok sayıda 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ştireceğiniz doğru eşiği bulmanıza yardımcı olmak için cihazları pencere boyutu sınıfları olarak adlandırılan önceden tanımlanmış boyut sınıflarına (kompakt, orta ve genişletilmiş) sınıflandırmaya yardımcı olan kesme noktası değerleri tanımladık. Bunlar, duyarlı ve uyarlanabilir uygulama düzenleri tasarlamanıza, geliştirmenize ve test etmenize yardımcı olan, önceden belirlenmiş bir dizi görüntü alanı kesme noktasıdır.

Kategoriler, düzen sadeliği ile uygulamanızı benzersiz durumlar için optimize etme esnekliğini dengeleyecek şekilde özel olarak seçilmiştir. Pencere boyutu sınıfı her zaman uygulamanın kullanabileceği ekran alanına göre belirlenir. Bu alan, çoklu görev veya diğer segmentasyonlar için fiziksel ekranın tamamı olmayabilir.

Kompakt, orta ve genişletilmiş genişlik için WindowWidthSizeClass.

Kompakt, orta ve genişletilmiş yükseklik için WindowHeightSizeClass.

Hem genişlik hem de yükseklik ayrı olarak sınıflandırılır. Bu nedenle, uygulamanızda her zaman biri genişlik, diğeri yükseklik için olmak üzere iki pencere boyutu sınıfı bulunur. 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, değişen boyutları ve menteşeleri nedeniyle uygulamanızın uyum sağlayabileceği daha fazla durum sunar. Menteşeler ekranın bir kısmını gizleyerek içerik göstermek için uygun olmayabilir. Menteşeler birbirinden ayrılabilir, yani cihaz açıldığında iki ayrı fiziksel ekran gösterilir.

Düz ve yarı açık katlanabilir duruşlar

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şları ve menteşeler hakkında daha fazla bilgi edinin.

Bunların tümü, katlanabilir cihazları destekleyen uyarlanabilir düzenler uygularken dikkate alınması gereken noktalardır.

Uyarlanabilir bilgiler alma

Material3 adaptive kitaplığı, uygulamanızın çalıştığı pencereyle ilgili bilgilere kolayca erişmenizi sağlar.

  1. Sürüm kataloğu dosyasına bu yapının ve sürümünün girişlerini ekleyin:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. Uygulama modülünün derleme dosyasına yeni kitaplık bağımlılığını ekleyin ve ardından Gradle senkronizasyonu gerçekleştirin:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

Artık herhangi bir derlenebilir kapsamda currentWindowAdaptiveInfo() kullanarak mevcut pencere boyutu sınıfı ve cihazın masaüstü gibi katlanabilir bir konumda olup olmadığı gibi bilgileri içeren bir WindowAdaptiveInfo nesnesi alabilirsiniz.

Bunu hemen MainActivity'da deneyebilirsiniz.

  1. ReplyTheme bloğunun içindeki onCreate() öğesinde, pencerenin uyarlanabilir bilgilerini alın ve boyut sınıflarını bir Text bileşeninde gösterin. 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(
                    WindowInsets.safeDrawing.asPaddingValues()
                )
            )
        }
    }
}

Uygulamayı çalıştırdığınızda artık uygulama içeriğinin üzerine pencere boyutu sınıfları basılı olarak gösterilir. Pencereye uyarlanabilir bilgilerde başka neler sağlandığını keşfedebilirsiniz. Ardından, uygulama içeriğini kapsadığı ve sonraki adımlar için gerekli olmadığı için bu Text öğesini kaldırabilirsiniz.

4. Dinamik gezinme

Şimdi, uygulamanızın kullanımını kolaylaştırmak için cihaz durumu ve boyutu değiştikçe uygulamanın gezinme menüsünü 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, ellerini aşırı konumlara getirmeden veya el yerleşimlerini değiştirmeden bir uygulamada gezinebilir veya uygulamayla etkileşim başlatabilir olmalıdır.

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ı tutarken hangi alanlara rahatça ulaşabiliyorsunuz?
  • Hangi alanlara yalnızca parmakları uzatarak erişilebilir ve bu durum rahatsız edici olabilir?
  • Hangi alanlara ulaşmak zordur veya kullanıcının cihazı tuttuğu yerden uzaktadır?

Gezinme, kullanıcıların etkileşime geçtiği ilk öğedir ve kritik kullanıcı yolculuklarıyla ilgili yüksek öneme sahip işlemler içerir. Bu nedenle, en kolay erişilebilen alanlara yerleştirilmelidir. Material uyarlanabilir kitaplığı, cihazın pencere boyutu sınıfına bağlı olarak gezinme 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 tuttuğumuz için alt gezinme bölümü en küçük boyutlar için idealdir. Kompakt boyutlu bir cihazınız veya katlanabilir cihazınız katlanmış durumdayken bu modu kullanın.

Öğeler içeren alt gezinme çubuğu

Orta genişlikteki bir pencere boyutu için gezinme çubuğu, başparmağımız doğal olarak cihazın yan tarafına düştüğü için erişilebilirlik açısından idealdir. Daha fazla bilgi göstermek için gezinme çubuğunu gezinme çekmeciyle de birleştirebilirsiniz.

Öğeler içeren gezinme çubuğu

Gezinme çekmecesi, gezinme sekmeleriyle ilgili ayrıntılı bilgileri kolayca görmenizi sağlar ve tabletler veya daha büyük cihazlar kullanırken kolayca erişilebilir. İki tür gezinme çekmecesi vardır: modal gezinme çekmecesi ve kalıcı gezinme çekmecesi.

Modal 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.

Öğeler içeren modal gezinme çekmecesi

Kalıcı gezinme çekmecesi

Büyük tabletlerde, Chromebook'larda ve masaüstü bilgisayarlarda sabit gezinme için kalıcı bir gezinme çekmecesi kullanabilirsiniz.

Öğeler içeren kalıcı gezinme çekmecesi

Dinamik gezinme uygulama

Artık cihaz durumu ve boyutu değiştikçe farklı gezinme türleri arasında geçiş yapacaksınız.

Şu anda uygulama, cihaz durumundan bağımsız olarak ekran içeriğinin altında her zaman bir NavigationBar gösterir. Bunun yerine, mevcut pencere boyutu sınıfı gibi bilgilere göre farklı gezinme bileşenleri arasında otomatik olarak geçiş yapmak için Materyal NavigationSuiteScaffold bileşenini kullanabilirsiniz.

  1. 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 Gradle senkronizasyonu gerçekleştirin:

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)
}
  1. ReplyApp.kt içinde ReplyNavigationWrapper() birleştirilebilir işlevini bulun ve Column ile içeriğini NavigationSuiteScaffold 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çine öğe eklemeye benzer şekilde item() işlevini kullanarak öğe eklemenize olanak tanıyan bir bloktur. Son lambda içinde bu kod, ReplyNavigationWrapperUI() işlevine bağımsız değişken olarak iletilen content() işlevini çağırır.

Uygulamayı emülatörde çalıştırın ve telefon, katlanabilir cihaz ve tablet arasında boyut değiştirmeyi deneyin. Gezinme çubuğunun bir gezinme çubuğuna ve tekrar çubuğuna dönüştüğünü göreceksiniz.

Çok geniş pencerelerde, örneğin tablette yatay durumda, kalıcı gezinme çekmecesini göstermek isteyebilirsiniz. NavigationSuiteScaffold, kalıcı bir çekmecenin gösterilmesini destekler ancak mevcut WindowWidthSizeClass değerlerinden hiçbirinde gösterilmez. Ancak küçük bir değişiklikle bunu başarabilirsiniz.

  1. 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 ve currentWindowSize() ile LocalDensity.current'ü kullanarak DP birimlerine dönüştürür. Ardından, 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 hesaplama kullanılır.

Uygulamayı yeniden boyutlandırılabilir emülatörünüzde çalıştırıp farklı türleri denediğinizde, ekran yapılandırması değiştiğinde veya katlanabilir bir cihazı açtığınızda navigasyonun o boyuta uygun türde değiştiğini fark edin.

Farklı boyutlardaki cihazlar için uyumluluk değişikliklerini gösterir.

Tebrikler, farklı pencere boyutlarını ve durumlarını desteklemek için farklı gezinme türleri hakkında bilgi edindiniz.

Sonraki bölümde, aynı liste öğesini kenardan kenara germek 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. Bu ekran alanından daha fazla bilgi göstermek için yararlanmak istersiniz. Örneğin, bu uygulamada kullanıcılara aynı sayfada e-posta ve ileti dizileri gösterebilirsiniz.

3. materyalde, her biri kompakt, orta ve genişletilmiş pencere boyutu sınıfları için yapılandırmalara sahip üç kanonik düzen tanımlanır. Liste Ayrıntısı standart düzeni bu kullanım alanı için idealdir ve ListDetailPaneScaffold olarak oluşturulabilir.

  1. Aşağıdaki bağımlılıkları ekleyerek ve Gradle senkronizasyonu gerçekleştirerek bu bileşeni alın:

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)
}
  1. ReplyApp.kt içinde ReplyAppContent() composable işlevini bulun. Bu işlev şu anda yalnızca ReplyListPane()'i çağırarak liste bölmesini gösterir. 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 eklersiniz:

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österileceği ve bu bölmede hangi içeriğin gösterileceği konusunda bazı kontroller sağlar. Bu kontroller daha sonra gösterilecektir.

ListDetailPaneScaffold, pencere genişliği boyut sınıfı genişletildiğinde 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ışı elde etmek için iskelet yönergesini ve gezgin tarafından sağlanan iskelet değerini kullanır.

Geriye kalan zorunlu parametreler, bölmeler için birleştirilebilir lambdalardır. ReplyListPane() ve ReplyDetailPane() (ReplyListContent.kt içinde bulunur), sırasıyla liste ve ayrıntı bölmesinin rollerini doldurmak için kullanılır. ReplyDetailPane() bir e-posta bağımsız değişkeni bekler. Bu nedenle, bu kod şimdilik ReplyHomeUIState içindeki e-posta listesinden ilk e-postayı kullanır.

Uygulamayı çalıştırın ve iki bölmeli düzeni görmek için emülatör görünümünü katlanabilir veya tablet olarak değiştirin (yönlendirmeyi de değiştirmeniz gerekebilir). Bu, öncekinden ç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. Uygulama şu anda hangi e-postanın seçildiğini takip etmiyor ve bir öğeye dokunulduğunda hiçbir işlem yapılmıyor. Bu bilgileri saklamak için en iyi yer, kullanıcı arayüzü durumunun geri kalanı ReplyHomeUIState şeklindedir.

  1. ReplyHomeViewModel.kt dosyasını açıp ReplyHomeUIState veri sınıfını bulun. Seçilen e-posta için varsayılan değeri null olan bir özellik ekleyin:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. Aynı dosyada ReplyHomeViewModel, kullanıcı bir liste öğesine dokunduğunda çağrılan bir setSelectedEmail() 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)
    }
}

Kullanıcı herhangi bir öğeye dokunmadan önce nelerin gerçekleştiğini ve seçilen e-postanın null olduğunu göz önünde bulundurun. Ayrıntılar bölmesinde ne gösterilmelidir? Bu durumu ele almanın birden fazla yolu vardır. Örneğin, listedeki ilk öğeyi varsayılan olarak gösterebilirsiniz.

  1. 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()
                )
            }
    }
}
  1. 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ı penceresinin içeriğinin güncellendiğini göreceksiniz.

Bu yöntem, her iki bölme de görünür durumdayken mükemmel şekilde çalışır. Ancak pencerede yalnızca bir bölmeyi gösterecek yer olduğunda, bir öğeye dokunduğunuzda hiçbir şey olmuyor gibi görünür. Emülatör görünümünü bir telefona veya dikey olarak katlanabilir bir cihaza geçirmeyi deneyin ve bir öğeye dokunduktan sonra bile yalnızca liste bölmesinin görünür olduğunu fark edin. Bunun nedeni, seçili e-posta güncellenmiş olsa bile ListDetailPaneScaffold'ün bu yapılandırmalarda odağını liste bölmesinde tutmasıdır.

  1. 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 gezgini kullanır. Bu işleve iletilen orijinal lambda işlevini çağırır ve ardından hangi bölmenin gösterileceğini belirten navigator.navigateTo() işlevini de çağırır. İskeletteki her bölmenin ilişkili bir rolü vardır. Ayrıntı bölmesi için bu rol ListDetailPaneScaffoldRole.Detail'tür. Daha küçük pencerelerde bu, uygulamanın ileriye dönük olduğunu gösterir.

Uygulamanın, kullanıcı ayrıntı penceresinden 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.

  1. 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 öğesinin tam durumunu, geri gezinmenin mümkün olup olmadığını ve tüm bu senaryolarda ne yapılması gerektiğini 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şin çok daha sorunsuz olması için her bölme bir AnimatedPane() bileşene sarılır.

Uygulamayı, tüm farklı cihaz türleri için yeniden boyutlandırılabilir bir emülatörde çalıştırın ve 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 yanıt olarak dinamik olarak değiştiğini fark edin. Ayrıca, liste bölmesinde e-postalara dokunmayı deneyin ve farklı ekranlarda düzenin nasıl davrandığını, her iki bölmeyi yan yana gösterip göstermediğini veya aralarında sorunsuz bir şekilde geçiş yapıp yapmadığını görün.

Farklı cihaz boyutları için uyarlanabilirlik değişiklikleri gösteriliyor.

Tebrikler, uygulamanızı tüm cihaz durumlarına ve boyutlarına uyumlu hale getirdiniz. Uygulamayı katlanabilir cihazlarda, tabletlerde veya diğer mobil cihazlarda çalıştırmayı deneyin.

6. Tebrikler

Tebrikler! Bu codelab'i başarıyla tamamladınız ve Jetpack Compose ile uygulamaların nasıl uyarlanabilir hale getirileceğini öğrendiniz.

Bir cihazın boyutunu ve katlama durumunu nasıl kontrol edeceğinizi, uygulamanızın kullanıcı arayüzünü, gezinme menüsünü ve diğer işlevlerini buna göre nasıl güncelleyeceğinizi öğ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'ndaki diğer kod laboratuvarlarına 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.

Referans dokümanları