Farklı ekran boyutlarını destekleme

Farklı ekran boyutlarının desteklenmesi, uygulamanıza en fazla sayıda cihaz ve en fazla kullanıcı sayısına göre erişebilmenizi sağlar.

Mümkün olduğunca fazla ekran boyutunu desteklemek için uygulama düzenlerinizi duyarlı ve uyarlanabilir olacak şekilde tasarlayın. Duyarlı/uyarlanabilir düzenler, ekran boyutundan bağımsız olarak optimize bir kullanıcı deneyimi sunarak uygulamanızın telefonlar, tabletler, katlanabilir cihazlar, ChromeOS cihazlar, dikey ve yatay yönler ve çoklu pencere modu gibi yeniden boyutlandırılabilir yapılandırmalara uymasını sağlar.

Duyarlı/uyarlanabilir düzenler, kullanılabilir görüntüleme alanına göre değişir. Uygulamanızın farklı ekran boyutlarına (uyarlanabilir tasarım) en iyi şekilde uymasını sağlamak için, alan kaplayan küçük düzen düzenlemelerinden (duyarlı tasarım) bir düzeni tamamen başka bir düzeniyle değiştirmeye kadar değişiklikler vardır.

Bildirim temelli bir kullanıcı arayüzü araç seti olan Jetpack Compose, içerikleri çeşitli ekran boyutlarında farklı şekillerde oluşturmak amacıyla dinamik olarak değişen düzenler tasarlayıp uygulamak için idealdir.

Ekran düzeyinde composable'lar için uygunsuz düzen değişiklikleri yapma

Bir uygulamanın tamamını yerleştirmek için Compose'u kullandığınızda uygulama düzeyinde ve ekran düzeyinde composable'lar, uygulamanıza oluşturmaları için verilen alanın tamamını kaplar. Tasarımınızda bu düzeyde, daha büyük ekranlardan yararlanmak için bir ekranın genel düzenini değiştirmek mantıklı olabilir.

Düzenle ilgili kararlar alırken fiziksel, donanım değerleri kullanmaktan kaçının. Kararlarınızı sabit bir somut değere göre vermek cazip gelebilir (Cihaz bir tablet mi? Fiziksel ekranın belirli bir en boy oranı var mı?) ancak bu sorulara verilen yanıtlar, kullanıcı arayüzünüzün çalışabileceği alanı belirlemek açısından faydalı olmayabilir.

Telefon, katlanabilir cihaz, tablet ve dizüstü bilgisayar gibi farklı cihaz form faktörlerini gösteren şema.
Şekil 1. Telefon, katlanabilir cihaz, tablet ve dizüstü bilgisayar form faktörleri

Tabletlerde bir uygulama çoklu pencere modunda çalışıyor olabilir. Bu da uygulamanın ekranı başka bir uygulamayla böldüğü anlamına gelir. ChromeOS'te, bir uygulama yeniden boyutlandırılabilir bir pencerede olabilir. Hatta, katlanabilir cihazlar gibi birden fazla fiziksel ekran bile olabilir. Tüm bu durumlarda, içeriğin nasıl görüntüleneceğine karar vermek için fiziksel ekran boyutu geçerli değildir.

Bunun yerine, Jetpack WindowManager kitaplığı tarafından sağlanan geçerli pencere metrikleri gibi ekranın uygulamanıza ayrılmış gerçek bölümünü temel alarak karar vermelisiniz. Compose uygulamasında WindowManager'ı nasıl kullanacağınızı öğrenmek için JetNews örneğine göz atın.

Bu yaklaşımı izlemek, uygulamanızı yukarıdaki tüm senaryolarda iyi davranacağı için daha esnek hale getirir. Düzenlerinizi ekran alanına uygun hale getirmek, ChromeOS gibi platformları ve tablet ve katlanabilir cihazlar gibi form faktörlerini destekleyen özel işleme miktarını da azaltır.

Uygulamanız için uygun alanı gözlemledikten sonra ham boyutu, Pencere boyutu sınıfları bölümünde açıklandığı gibi anlamlı bir boyut sınıfına dönüştürmek faydalıdır. Bu boyutlar, standart boyutlu gruplar halinde gruplandırılır. Bu gruplar, sadeliği ve uygulamanızı benzersiz durumların çoğu için optimize etme esnekliğini dengelemek için tasarlanmış ayrılma noktalarıdır. Bu boyut sınıfları, uygulamanızın genel penceresini ifade eder. Bu nedenle, genel ekran düzeninizi etkileyen düzen kararları için bu sınıfları kullanın. Bu boyut sınıflarını durum olarak aşağı aktarabilir veya türetilmiş durumu, iç içe yerleştirilmiş composable'lara aktaracak şekilde oluşturmak için ek mantık işlemleri gerçekleştirebilirsiniz.

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
    // Perform logic on the size class to decide whether to show the top app bar.
    val showTopAppBar = windowSizeClass.windowHeightSizeClass != WindowHeightSizeClass.COMPACT

    // MyScreen knows nothing about window sizes, and performs logic based on a Boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

Bu katmanlı yaklaşım, ekran boyutu mantığını tek bir konumla sınırlandırır. Bunun yerine, ekran boyutunu senkronize edilmesi gereken birçok yere uygulamanız genelinde dağıtmak yerine tek bir konumla sınırlandırır. Bu tek konum, diğer uygulama durumlarında olduğu gibi açık bir şekilde diğer composable'lara aktarılabilecek durum oluşturur. Durumu açık bir şekilde aktarmak, diğer verilerle birlikte boyut sınıfını veya belirtilen yapılandırmayı alan normal composable işlevler oldukları için bağımsız composable'ları basitleştirir.

Esnek iç içe yerleştirilmiş composable'lar yeniden kullanılabilir

Özelleştirilebilir öğeler, çok çeşitli yerlere yerleştirilebildiğinde daha fazla yeniden kullanılabilir. Bir composable her zaman belirli bir boyuta sahip belirli bir konuma yerleştirileceğini varsayarsa farklı bir yerde veya farklı miktarda kullanılabilir alanla başka bir yerde yeniden kullanılması daha zor olur. Bu aynı zamanda, bireysel, yeniden kullanılabilir composable'ların "global" boyut bilgilerine bağlı olarak dolaylı yoldan kaçınması gerektiği anlamına gelir.

Şu örneği düşünün: Bir bölmeyi veya iki bölmeyi yan yana gösterebilen list-detail düzeni uygulayan iç içe yerleştirilmiş bir composable düşünün.

İki bölmeyi yan yana gösteren bir uygulamanın ekran görüntüsü.
Şekil 2. Tipik bir liste ayrıntısı düzenini gösteren bir uygulamanın ekran görüntüsü. 1 liste alanı, 2 ise ayrıntı alanıdır.

Bu kararın, uygulama düzeninin genel bir parçası olmasını istiyoruz. Bu nedenle, yukarıda gördüğümüz gibi bu kararı, ekran düzeyinde bir composable'dan aktarıyoruz:

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

Bunun yerine, bir composable'ın düzenini mevcut alana göre bağımsız olarak değiştirmesini istersek ne olur? Örneğin, alan izin veriyorsa ek ayrıntılar göstermek isteyen bir kart. Mevcut bir boyuta dayanarak bazı mantık işlemleri uygulamak istiyoruz, ama tam olarak hangi boyuta?

İki farklı kart örnekleri.
Şekil 3. Yalnızca simge ve başlığın gösterildiği dar kart; simge, başlık ve kısa açıklamayı gösteren daha geniş bir kart.

Yukarıda gördüğümüz gibi, cihazın gerçek ekranının boyutunu kullanmaya çalışmamalıyız. Bu, birden fazla ekran için doğru olmaz ve uygulama tam ekran değilse de doğru olmayabilir.

composable, ekran düzeyinde bir composable olmadığından, yeniden kullanılabilirliği en üst düzeye çıkarmak için doğrudan geçerli pencere metriklerini kullanmamalıyız. Bileşen dolguyla yerleştiriliyorsa (ör. ek öğeler için) veya gezinme çubukları ya da uygulama çubukları gibi bileşenler varsa composable'ın kullanabileceği toplam alan, uygulamanın kullanabileceği toplam alandan önemli ölçüde farklı olabilir.

Bu nedenle, kendisini oluşturmak için composable'a verilen genişliği kullanmalıyız. Bu genişliği elde etmek için iki seçenek vardır:

İçeriğin nerede veya nasıl görüntüleneceğini değiştirmek istiyorsanız düzeni duyarlı hale getirmek için bir değiştirici koleksiyonu veya özel düzen kullanabilirsiniz. Bu, mevcut alanın tamamını birkaç çocukla doldurması veya yeterli alan varsa alt öğeleri birden çok sütunla yerleştirmek gibi basit bir yöntem olabilir.

Gösterdiğiniz öğeyi değiştirmek isterseniz daha güçlü bir alternatif olarak BoxWithConstraints kullanabilirsiniz. Bu composable, mevcut alana göre farklı composable'ları çağırmak için kullanabileceğiniz ölçüm kısıtlamaları sağlar. Ancak bu durum bazı maliyetleri beraberinde getirir. Çünkü BoxWithConstraints, Düzen aşamasına kadar kompozisyonu erteler. Bu aşama, bu kısıtlamalar bilindiği için düzen sırasında daha fazla işin yürütülmesine neden olur.

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

Tüm verilerin farklı boyutlar için sunulduğundan emin olun

Ek ekran alanından yararlanırken, büyük ekranda kullanıcıya küçük ekrana göre daha fazla içerik göstermek için yeriniz olabilir. Bu davranışla bir composable uygularken verimli olmak ve verileri mevcut boyutun yan etkisi olarak yüklemek cazip gelebilir.

Ancak bu, verilerin uygun şekilde oluşturulabileceği ve composable'a sunulabildiği tek yönlü veri akışı ilkelerine aykırıdır. Bir composable'ın, her boyutta görüntülemesi gerekene (verilerin bir kısmı her zaman kullanılmasa bile) her zaman sahip olması için yeterli miktarda veri sağlanmalıdır.

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

Card örneğinden yola çıkarak, description öğesinin her zaman Card öğesine iletildiğini unutmayın. description, yalnızca genişliğin gösterilmesine izin verdiğinde kullanılabilse de, kullanılabilir genişlik ne olursa olsun Card her zaman bunu gerektirir.

Verilerin her zaman aktarılması, uyarlanabilir düzenleri daha az durum bilgili hale getirerek daha basit hale getirir ve boyutlar arasında geçiş yaparken yan etkilerin tetiklenmesini önler (bu durum pencerenin yeniden boyutlandırması, yön değişikliği veya cihazın katlanıp açılması gibi nedenlerden kaynaklanabilir).

Bu ilke, düzen değişiklikleri genelinde durumun korunmasına da olanak tanır. Tüm boyutlarda kullanılamayan bilgileri kaldırarak düzen boyutu değiştikçe kullanıcının durumunu koruyabiliriz. Örneğin, yeniden boyutlandırmalar düzenin gizleme ile açıklamayı gösterme arasında geçiş yapmasına neden olduğunda kullanıcının durumunun korunması için showMore Boole işaretini kaldırabiliriz:

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

Daha fazla bilgi

Compose'daki özel düzenler hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın.

Örnek uygulamalar

  • Büyük ekran standart düzenleri, büyük ekranlı cihazlarda optimum kullanıcı deneyimi sağlayan, kanıtlanmış tasarım kalıplarının yer aldığı bir depodur
  • JetNews, kullanıcı arayüzünü mevcut alandan en iyi şekilde yararlanacak şekilde uyarlayan bir uygulamanın nasıl tasarlanacağını gösteriyor
  • Reply; cep telefonu, tablet ve katlanabilir cihazları desteklemek için uyarlanabilir bir örnektir.
  • Şimdi Android'de, farklı ekran boyutlarını desteklemek için uyarlanabilir düzenler kullanan bir uygulamadır.

Videolar