Android Uygulaması Yeniden Boyutlandırma

1. Giriş

Android cihaz ekosistemi sürekli olarak gelişmektedir. Donanım klavyelerin ilk günlerinden katlanabilir cihazlar, tabletler ve serbest biçimli yeniden boyutlandırılabilir pencerelerin modern dünyasına kadar Android uygulamaları, hiçbir zaman günümüzdeki kadar çeşitli cihazlarda çalışmamıştır.

Bu, geliştiriciler için harika bir haber olsa da kullanılabilirlik beklentilerini karşılamak ve farklı ekran boyutlarında mükemmel bir kullanıcı deneyimi sunmak için belirli uygulama optimizasyonları yapılması gerekir. Her yeni cihazı tek tek hedeflemek yerine, duyarlı/uyarlanabilir bir kullanıcı arayüzü ve esnek bir mimari, uygulamanızın mevcut ve gelecekteki kullanıcılarınızın bulunduğu her yerde (her boyutta ve şekildeki cihazlarda) harika görünmesine ve çalışmasına yardımcı olabilir.

Serbest biçimli yeniden boyutlandırılabilir Android ortamlarının kullanıma sunulması, duyarlı/uyarlanabilir kullanıcı arayüzünüzü herhangi bir cihaza hazır hale getirmek için stres testine tabi tutmanın harika bir yoludur. Bu kod laboratuvarı, yeniden boyutlandırmanın etkilerini anlamanıza ve uygulamanın sağlam ve kolay bir şekilde yeniden boyutlandırılmasını sağlamak için bazı en iyi uygulamaları uygulamanıza yardımcı olacaktır.

Ne oluşturacaksınız?

Serbest biçimli yeniden boyutlandırmanın etkilerini inceleyecek ve yeniden boyutlandırmayla ilgili en iyi uygulamaları göstermek için bir Android uygulamasını optimize edeceksiniz. Uygulamanız şunları yapabilecek:

Uyumlu bir manifest dosyasına sahip olma

  • Uygulamanın serbestçe yeniden boyutlandırılmasını engelleyen kısıtlamaları kaldırma

Boyut değiştirildiğinde durumu koruma

  • rememberSaveable kullanarak yeniden boyutlandırıldığında kullanıcı arayüzü durumunu korur.
  • Kullanıcı arayüzünü başlatmak için arka plan çalışmalarını gereksiz yere kopyalamaktan kaçının.

İhtiyacınız olanlar

  1. Temel Android uygulamaları oluşturma bilgisi
  2. Compose'da ViewModel ve State hakkında bilgi sahibi olmak
  3. Aşağıdakilerden biri gibi serbest biçimli pencere yeniden boyutlandırmayı destekleyen bir test cihazı:

Bu codelab'i uygularken herhangi bir sorunla (kod hataları, dilbilgisi hataları, net olmayan ifadeler vb.) karşılaşırsanız lütfen codelab'in sol alt köşesindeki Hata bildir bağlantısını kullanarak sorunu bildirin.

2. Başlarken

Depoyu GitHub'dan kopyalayın.

git clone https://github.com/android/large-screen-codelabs/

...veya depoyu ZIP dosyası olarak indirip ayıklayın.

Projeyi İçe Aktarma

  • Android Studio'yu açın.
  • Projeyi İçe Aktar veya Dosya->Yeni->Projeyi İçe Aktar'ı seçin.
  • Projeyi klonladığınız veya çıkardığınız yere gidin.
  • Yeniden boyutlandırma klasörünü açın.
  • Projeyi start klasöründe açın. Bu dosya, başlangıç kodunu içerir.

Uygulamayı deneyin

  • Uygulamayı derleyip çalıştırma
  • Uygulamanın boyutunu değiştirmeyi deneyin.

Ne düşünüyorsunuz?

Test cihazınızın uyumluluk desteğine bağlı olarak, kullanıcı deneyiminin ideal olmadığını fark etmiş olabilirsiniz. Uygulama yeniden boyutlandırılamıyor ve ilk en boy oranında takılı kalıyor. What is happening?

Manifest kısıtlamaları

Uygulamanın AndroidManifest.xml dosyasına baktığınızda, uygulamamızın serbest biçimli pencere yeniden boyutlandırma ortamında iyi davranmasını engelleyen birkaç kısıtlama eklendiğini görebilirsiniz.

AndroidManifest.xml

            android:maxAspectRatio="1.4"
            android:resizeableActivity="false"
            android:screenOrientation="portrait">

Bu üç sorunlu satırı manifestinizden kaldırmayı, uygulamayı yeniden oluşturmayı ve test cihazınızda tekrar denemeyi deneyin. Uygulamanın artık serbest biçimli yeniden boyutlandırma ile kısıtlanmadığını görürsünüz. Bu tür kısıtlamaları manifestinizden kaldırmak, uygulamanızı serbest biçimli pencere yeniden boyutlandırma için optimize etme konusunda önemli bir adımdır.

3. Yeniden boyutlandırmanın yapılandırma değişiklikleri

Uygulamanızın penceresi yeniden boyutlandırıldığında uygulamanızın yapılandırması güncellenir. Bu güncellemeler uygulamanızı etkiler. Bu güncellemeleri anlayıp öngörmek, kullanıcılarınıza mükemmel bir deneyim sunmanıza yardımcı olabilir. En belirgin değişiklikler, uygulama pencerenizin genişliği ve yüksekliğidir. Ancak bu değişiklikler, en boy oranı ve yönlendirme için de sonuçlar doğurur.

Yapılandırma değişikliklerini gözlemleme

Android görünüm sistemiyle oluşturulmuş bir uygulamada bu değişikliklerin nasıl gerçekleştiğini görmek için View.onConfigurationChanged işlevini geçersiz kılabilirsiniz. Jetpack Compose'da, View.onConfigurationChanged çağrıldığında otomatik olarak güncellenen LocalConfiguration.current erişimimiz vardır.

Bu yapılandırma değişikliklerini örnek uygulamanızda görmek için uygulamanıza LocalConfiguration.current değerlerini gösteren bir composable ekleyin veya böyle bir composable'ın bulunduğu yeni bir örnek proje oluşturun. Bunları görmek için örnek bir kullanıcı arayüzü şu şekilde olabilir:

val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation ==
    Configuration.ORIENTATION_PORTRAIT
val screenLayoutSize =
        when (configuration.screenLayout and
                Configuration.SCREENLAYOUT_SIZE_MASK) {
            SCREENLAYOUT_SIZE_SMALL -> "SCREENLAYOUT_SIZE_SMALL"
            SCREENLAYOUT_SIZE_NORMAL -> "SCREENLAYOUT_SIZE_NORMAL"
            SCREENLAYOUT_SIZE_LARGE -> "SCREENLAYOUT_SIZE_LARGE"
            SCREENLAYOUT_SIZE_XLARGE -> "SCREENLAYOUT_SIZE_XLARGE"
            else -> "undefined value"
        }
Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()
) {
    Text("screenWidthDp: ${configuration.screenWidthDp}")
    Text("screenHeightDp: ${configuration.screenHeightDp}")
    Text("smallestScreenWidthDp: ${configuration.smallestScreenWidthDp}")
    Text("orientation: ${if (isPortrait) "portrait" else "landscape"}")
    Text("screenLayout SIZE: $screenLayoutSize")
}

Örnek bir uygulamayı observing-configuration-changes proje klasöründe görebilirsiniz. Bunu uygulamanızın kullanıcı arayüzüne eklemeyi deneyin, test cihazınızda çalıştırın ve uygulamanızın yapılandırması değiştikçe kullanıcı arayüzünün güncellenmesini izleyin.

Uygulama yeniden boyutlandırıldığında, değişen yapılandırma bilgileri uygulamanın arayüzünde anlık olarak gösterilir.

Uygulamanızın yapılandırmasında yapılan bu değişiklikler, küçük bir telefonda bölünmüş ekranla beklediğimiz uç noktalardan tablete veya masaüstüne geçişi hızlı bir şekilde simüle etmenize olanak tanır. Bu, uygulamanızın düzenini farklı ekranlarda test etmenin iyi bir yolu olmasının yanı sıra uygulamanızın hızlı yapılandırma değişikliği etkinliklerini ne kadar iyi işleyebildiğini test etmenize de olanak tanır.

4. Etkinlik yaşam döngüsü olaylarını günlüğe kaydetme

Uygulamanız için serbest biçimli pencere yeniden boyutlandırmanın bir diğer sonucu da uygulamanızda meydana gelecek çeşitli Activity yaşam döngüsü değişiklikleridir. Bu değişiklikleri gerçek zamanlı olarak görmek için onCreate yönteminize bir yaşam döngüsü gözlemcisi ekleyin ve onStateChanged'i geçersiz kılarak her yeni yaşam döngüsü olayını günlüğe kaydedin.

lifecycle.addObserver(object : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        Log.d("resizing-codelab-lifecycle", "$event was called")
    }
})

Bu günlük kaydı etkin durumdayken uygulamanızı test cihazınızda tekrar çalıştırın ve uygulamanızı küçültüp tekrar ön plana getirmeye çalışırken logcat'e bakın.

Uygulamanızın küçültüldüğünde duraklatıldığını, ön plana getirildiğinde ise tekrar devam ettirildiğini gözlemleyin. Bu durum, uygulamanız için sonuçlar doğurur. Bu sonuçları, bu codelab'in sürekliliğe odaklanan bir sonraki bölümünde inceleyeceğiz.

Yeniden boyutlandırma sırasında etkinlik yaşam döngüsü yöntemlerinin çağrıldığını gösteren logcat

Şimdi Logcat'e bakarak uygulamanızı mümkün olan en küçük boyuttan en büyük boyuta yeniden boyutlandırdığınızda hangi etkinlik yaşam döngüsü geri çağırmalarının çağrıldığını görün.

Test cihazınıza bağlı olarak farklı davranışlar gözlemleyebilirsiniz. Ancak muhtemelen uygulamanızın pencere boyutu önemli ölçüde değiştirildiğinde etkinliğinizin yok edilip yeniden oluşturulduğunu, ancak boyutu biraz değiştirildiğinde etkinliğinizin yok edilip yeniden oluşturulmadığını fark etmişsinizdir. Bunun nedeni, API 24 ve sonraki sürümlerde yalnızca önemli boyut değişikliklerinin Activity yeniden oluşturulmasına neden olmasıdır.

Serbest biçimli pencere oluşturma ortamında bekleyebileceğiniz bazı yaygın yapılandırma değişikliklerini gördünüz ancak dikkat etmeniz gereken başka değişiklikler de var. Örneğin, test cihazınıza bağlı harici bir monitörünüz varsa Activity'nizin, ekran yoğunluğu gibi yapılandırma değişikliklerini hesaba katmak için yok edilip yeniden oluşturulduğunu görebilirsiniz.

Yapılandırma değişiklikleriyle ilişkili karmaşıklığı azaltmak için uyarlanabilir kullanıcı arayüzünüzü uygularken WindowSizeClass gibi daha üst düzey API'leri kullanın. (Ayrıca Farklı ekran boyutlarını destekleme başlıklı makaleyi de inceleyin.)

5. Devamlılık: Yeniden boyutlandırıldığında composable'ların dahili durumunu koruma

Önceki bölümde, uygulamanızın serbest biçimli pencere yeniden boyutlandırma ortamında karşılaşabileceği bazı yapılandırma değişikliklerini görmüştünüz. Bu bölümde, uygulamanızın kullanıcı arayüzü durumunu bu değişiklikler boyunca sürekli hale getireceksiniz.

NavigationDrawerHeader composable işlevini (ReplyHomeScreen.kt içinde bulunur) tıklanınca e-posta adresini gösterecek şekilde genişleterek başlayın.

@Composable
private fun NavigationDrawerHeader(
    modifier: Modifier = Modifier
) {
    var showDetails by remember { mutableStateOf(false) }
    Column(
        modifier = modifier.clickable {
                showDetails = !showDetails
            }
    ) {


        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            ReplyLogo(
                modifier = Modifier
                    .size(dimensionResource(R.dimen.reply_logo_size))
            )
            ReplyProfileImage(
                drawableResource = LocalAccountsDataProvider
                    .userAccount.avatar,
                description = stringResource(id = R.string.profile),
                modifier = Modifier
                    .size(dimensionResource(R.dimen.profile_image_size))
            )
        }
        AnimatedVisibility (showDetails) {
            Text(
                text = stringResource(id = LocalAccountsDataProvider
                        .userAccount.email),
                style = MaterialTheme.typography.labelMedium,
                modifier = Modifier
                    .padding(
                        start = dimensionResource(
                            R.dimen.drawer_padding_header),
                        end = dimensionResource(
                            R.dimen.drawer_padding_header),
                        bottom = dimensionResource(
                            R.dimen.drawer_padding_header)
                ),


            )
        }
    }
}

Uygulamanıza genişletilebilir başlığı eklediğinizde,

  1. Uygulamayı test cihazınızda çalıştırma
  2. başlığa dokunarak genişletme
  3. pencereyi yeniden boyutlandırmayı deneyin

Üstbilginin boyutu önemli ölçüde değiştirildiğinde durumunu kaybettiğini görürsünüz.

Uygulamanın gezinme çekmecesi başlığına dokunuluyor ve başlık genişliyor ancak uygulama yeniden boyutlandırıldıktan sonra daralıyor

remember, yeniden oluşturma işlemleri arasında durumu korumanıza yardımcı olur ancak etkinlik veya süreç yeniden oluşturma işlemleri arasında durumu korumanıza yardımcı olmaz. Bu nedenle kullanıcı arayüzü durumu kaybolur. Durum yükseltme (state hoisting) yaygın olarak kullanılır. Bu teknikte, composable'ları durum bilgisiz hale getirmek için durum, composable'ın çağıranına taşınır. Bu sayede sorun tamamen önlenebilir. Bununla birlikte, kullanıcı arayüzü öğesi durumunu composable işlevlerde dahili olarak tutarken remember kullanabilirsiniz.

Bu sorunları çözmek için remember yerine rememberSaveable yazın. Bu işlem, rememberSaveable hatırlanan değeri savedInstanceState'ye kaydedip geri yüklediği için çalışır. remember değerini rememberSaveable olarak değiştirin, uygulamanızı test cihazında çalıştırın ve uygulamayı tekrar yeniden boyutlandırmayı deneyin. Genişletilebilir başlığın durumunun, yeniden boyutlandırma işlemi boyunca amaçlandığı gibi korunduğunu görürsünüz.

6. Arka plan çalışmalarının gereksiz yere yinelenmesini önleme

rememberSaveable kullanarak, serbest biçimli pencere yeniden boyutlandırma nedeniyle sık sık gerçekleşebilen yapılandırma değişiklikleri sırasında composable'ların dahili kullanıcı arayüzü durumunu nasıl koruyabileceğinizi gördünüz. Ancak bir uygulama genellikle kullanıcı arayüzü durumunu ve mantığını composable'lardan uzaklaştırmalıdır. Yeniden boyutlandırma sırasında durumu korumanın en iyi yollarından biri durum sahipliğini bir ViewModel'e taşımaktır. Durumunuzu ViewModel'ya yükseltirken dosya sistemine yoğun erişim veya ekranınızı başlatmak için gerekli olan ağ çağrıları gibi uzun süren arka plan işlemleriyle ilgili sorunlarla karşılaşabilirsiniz.

Karşılaşabileceğiniz sorun türlerinin bir örneğini görmek için ReplyViewModel içindeki initializeUIState yöntemine bir günlük ifadesi ekleyin.

fun initializeUIState() {
    Log.d("resizing-codelab", "initializeUIState() called in the viewmodel")
    val mailboxes: Map<MailboxType, List<Email>> =
        LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
    _uiState.value =
        ReplyUiState(
            mailboxes = mailboxes,
            currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
                ?: LocalEmailsDataProvider.defaultEmail
        )
}

Şimdi uygulamayı test cihazınızda çalıştırın ve uygulamanızın penceresini birkaç kez yeniden boyutlandırmayı deneyin.

Logcat'e baktığınızda uygulamanızın başlatma yönteminin birkaç kez çalıştığını görürsünüz. Bu durum, kullanıcı arayüzünüzü başlatmak için yalnızca bir kez çalıştırmak istediğiniz işlerde sorun yaratabilir. Ek ağ çağrıları, dosya G/Ç'si veya diğer işlemler cihazın performansını olumsuz etkileyebilir ve başka istenmeyen sorunlara neden olabilir.

Gereksiz arka plan çalışmalarını önlemek için etkinliğinizin onCreate() yönteminden initializeUIState() çağrısını kaldırın. Bunun yerine, verileri ViewModel öğesinin init yönteminde başlatın. Bu, başlatma yönteminin yalnızca ReplyViewModel ilk kez oluşturulduğunda bir kez çalıştırılmasını sağlar:

init {
    initializeUIState()
}

Uygulamayı tekrar çalıştırmayı deneyin. Uygulamanızın penceresini kaç kez yeniden boyutlandırırsanız boyutlandırın, gereksiz simüle edilmiş başlatma görevinin yalnızca bir kez çalıştığını görebilirsiniz. Bunun nedeni, ViewModels'in Activity yaşam döngüsünün ötesinde kalıcı olmasıdır. Başlatma kodunu yalnızca ViewModel oluşturulurken bir kez çalıştırarak kodu Activity yeniden oluşturma işlemlerinden ayırır ve gereksiz çalışmayı önleriz. Bu işlem aslında pahalı bir sunucu çağrısı veya kullanıcı arayüzünüzü başlatmak için ağır bir dosya G/Ç işlemi olsaydı önemli ölçüde kaynak tasarrufu sağlar ve kullanıcı deneyiminizi iyileştirirdiniz.

7. TEBRİKLER!

Başardınız! İyi iş çıkardınız! Artık Android uygulamalarının ChromeOS'te ve diğer çok pencereli, çok ekranlı ortamlarda iyi şekilde yeniden boyutlandırılmasını sağlamak için bazı en iyi uygulamaları uyguladınız.

Örnek Kaynak Kodu

Depoyu GitHub'dan klonlayın.

git clone https://github.com/android/large-screen-codelabs/

...veya depoyu ZIP dosyası olarak indirip ayıklayın.