Jetpack Compose ile uyarlanabilir uygulamalar geliştirme

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

Bu codelab için farklı cihaz türleri ve pencere boyutları arasında geçiş yapmanızı sağlayan Yeniden boyutlandırılabilir emülatörü kullanacaksınız.

Telefon, açık, tablet ve masaüstü seçenekleriyle yeniden boyutlandırılabilen emülatör.

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ı Materyal gezinme özellikleri ve optimum ekran alanı kullanımı için en iyi uygulamalardan yararlanan etkileşimli bir e-posta istemci uygulaması.

Bu codelab'de elde edeceğiniz birden çok cihaz desteği örneği

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ç

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

Öncelikle MainActivity.kt öğesine odaklanacaksınız.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )
        }
    }
}

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.

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

Kompakt, orta ve genişletilmiş genişlik için WindowWidthSizeClass özelliğini kullanın.

Kompakt, orta ve genişletilmiş yükseklik için WindowHeightSizeClass değerini girin.

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 kullanılabilir yükseklikten daha önemlidir. Bu durumda genişlik boyutu sınıflarını da kullanırsı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ç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ş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.

  1. 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" }
  1. 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.

  1. ReplyTheme bloğunun içindeki onCreate() içinde, pencereye uyarlanabilir bilgileri alın ve boyut sınıflarını bir Text 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. Penceredeki uyarlanabilir bilgiler bölümünde sağlanan diğer bilgileri inceleyebilirsiniz. Sonrasında, uygulama içeriğini kapsadığından bu Text öğesini kaldırabilirsiniz ve sonraki adımlarda gerekli değildir.

4. Dinamik gezinme

Artık erişilebilirliği iyileştirmek için cihaz durumu ve boyutu değiştikçe uygulamada gezinmeyi uyarlayacaksınız.

Erişilebilirlik, ekstrem el pozisyonları ya da el yerleşimlerini değiştirmeden bir uygulamada gezinme veya uygulamayla etkileşim başlatma yeteneğidir. 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. 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. Materyal, 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.

Öğelerin olduğu alt gezinme çubuğu

Orta genişlikli bir pencere boyutu için, baş parmağımız doğal olarak cihazın yan tarafına doğru uzandığından gezinme çubuğu erişilebilirlik açısından ideal bir çözümdür. Daha fazla bilgi göstermek için gezinme çubuğunu bir gezinme çekmecesiyle de birleştirebilirsiniz.

Öğe içeren gezinme çubuğu

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.

Öğelerin olduğu kalıcı 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ş 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.

  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 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)
}
  1. ReplyApp.kt içinde ReplyNavigationWrapper() composable işlevini bulup Column öğesini ve 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ç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.

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

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

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.

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

  1. Aşağıdaki bağımlılıkları ekleyip Gradle senkronizasyonu gerçekleştirerek bu bileşeni edinin:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[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. Şu anda yalnızca ReplyListPane() öğesini çağırarak liste bölmesini gösteren ReplyApp.kt öğesinde ReplyAppContent() 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.

  1. ReplyHomeViewModel.kt sayfasını açın ve ReplyHomeUIState veri sınıfını bulun. Seçili e-posta için varsayılan değeri null 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
)
  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)
    }
}

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.

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

  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 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 olduğunu 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)
                }
            }
        }
    )
}

Kılavuz, ListDetailPaneScaffold cihazının tüm durumunu, geri navigasyonun 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.

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

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.

Sonraki adım

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.

Referans belgeler