Farklı ekran boyutlarını destekleme

Farklı ekran boyutlarını desteklemek, uygulamanıza en geniş cihaz çeşitliliğinden ve en fazla sayıda kullanıcıdan erişim 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 edilmiş bir kullanıcı deneyimi sunar. Böylece uygulamanız telefonlar, tabletler, katlanabilir cihazlar, ChromeOS cihazlar, dikey ve yatay yönler ve çoklu pencere modu gibi yeniden boyutlandırılabilir yapılandırmalara uyum sağlayabilir.

Duyarlı/uyarlanabilir düzenler, kullanılabilir görüntüleme alanına göre değişir. Değişiklikler, alanı dolduran küçük düzen ayarlarından (duyarlı tasarım) uygulamanızın farklı ekran boyutlarına en iyi şekilde uyum sağlayabilmesi için bir düzenin tamamen başka bir düzenle değiştirilmesine (uyarlanabilir tasarım) kadar çeşitlilik gösterir.

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ı tasarlamak için Compose'u kullandığınızda uygulama düzeyindeki ve ekran düzeyindeki bileşenler, uygulamanızın oluşturması için verilen tüm alanı kaplar. Tasarımınızın bu düzeyinde, daha büyük ekranlardan yararlanmak için ekranın genel düzenini değiştirmek yararlı olabilir.

Düzenle ilgili kararlar alırken fiziksel, donanım değerleri kullanmaktan kaçının. Sabit ve somut bir değere (Cihaz tablet mi? Fiziksel ekranın belirli bir en boy oranı var mı?), ancak bu soruların yanıtları, kullanıcı arayüzünüzün çalışabileceği alanı belirlemek için yararlı olmayabilir.

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

Tabletlerde bir uygulama, çoklu pencere modunda çalışıyor olabilir. Bu durumda uygulama, ekranı başka bir uygulamayla paylaşıyor olabilir. ChromeOS'te bir uygulama, yeniden boyutlandırılabilir bir pencerede olabilir. Katlanabilir cihazlarda olduğu 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, ekranın uygulamanıza ayrılan gerçek bölümüne göre karar vermelisiniz (ör. Jetpack WindowManager kitaplığı tarafından sağlanan mevcut pencere metrikleri). WindowManager'ın Compose uygulamasında nasıl kullanıldığını görmek 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ını kullanma bölümünde açıklandığı gibi anlamlı bir boyut sınıfına dönüştürmek faydalı olacaktı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 iletebilir veya iç içe yerleştirilmiş kompozisyonlara iletilecek türetilmiş durum oluşturmak için ek mantık yürütebilirsiniz.

@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ı uygulamanızda senkronize tutulması gereken birçok yere dağıtmak yerine tek bir konuma sınırlandırır. Bu tek konum, diğer tüm uygulama durumlarında olduğu gibi diğer derlenebilir öğelere açıkça aktarılabilen bir 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ş kompozisyonlar yeniden kullanılabilir

Özelleştirilebilir öğeler, çok çeşitli yerlere yerleştirilebildiğinde daha fazla yeniden kullanılabilir. Bir bileşiğin her zaman belirli bir boyuta sahip olarak belirli bir yere yerleştirileceği varsayılırsa bu bileşiği başka bir yerde, farklı bir boyuta sahip olarak veya farklı bir alanda yeniden kullanmak daha zor olur. Bu, tek tek yeniden kullanılabilir bileşenlerin "genel" boyut bilgilerine dolaylı olarak bağımlı olmaktan kaçınması gerektiği anlamına da gelir.

Aşağıdaki örneği düşünün: Yan yana bir veya iki bölme gösterebilen liste-ayrıntı düzenini uygulayan iç içe yerleştirilmiş bir kompozisyon 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ı 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 göre bazı mantık işlemleri yapmak istiyoruz ancak hangi boyuta göre?

İki farklı kart örneği.
Ş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ışmaktan kaçınmalıyız. Bu değer, birden fazla ekran için ve uygulama tam ekran değilse doğru olmaz.

Kompozit, ekran düzeyinde bir kompozit olmadığından yeniden kullanılabilirliği en üst düzeye çıkarmak için mevcut pencere metriklerini de doğrudan kullanmamalıyız. Bileşen, dolguyla yerleştiriliyorsa (ör. içe yerleştirilmiş öğeler için) veya gezinme çubuğu ya da uygulama çubuğu gibi bileşenler varsa bileşenin kullanabileceği alan miktarı, uygulamanın kullanabileceği toplam alandan önemli ölçüde farklı olabilir.

Bu nedenle, derlenebilir öğenin kendisini oluşturmak için aslında 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österildiğini değiştirmek istiyorsanız düzeni duyarlı hale getirmek için bir değiştirici koleksiyonu veya özel bir düzen kullanabilirsiniz. Bu, bazı çocukların mevcut alanı doldurması veya yeterli alan varsa çocukları birden fazla sütunda düzenlemek kadar basit olabilir.

Gösterdiğiniz öğeleri değiştirmek istiyorsanız daha güçlü bir alternatif olarak BoxWithConstraints öğesini kullanabilirsiniz. Bu composable, mevcut alana göre farklı composable'ları çağırmak için kullanabileceğiniz ölçüm kısıtlamaları sağlar. Ancak BoxWithConstraints, bu kısıtlamaların bilindiği düzen aşamasına kadar kompozisyonu ertelediğinden ve düzen sırasında daha fazla çalışma yapılmasına neden olduğundan bu işlem bazı maliyetlere 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 kıyasla daha fazla içerik gösterebilirsiniz. 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şturulması için toplanarak bileşenlere sağlanabileceği tek yönlü veri akışı ilkelerine aykırıdır. Verilerin bir kısmı her zaman kullanılmasa bile, birleştirilebilir öğenin her zaman herhangi bir boyutta görüntülemek için ihtiyaç duyduğu verilere sahip olması amacıyla birleştirilebilir öğeye yeterli 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ğini temel alarak, description değerinin her zaman Card değerine iletildiğini unutmayın. description yalnızca genişlik gösterilmesine izin verdiğinde kullanılsa da Card, mevcut genişlikten bağımsız olarak her zaman bunu gerektirir.

Verileri her zaman iletmek, durum bilgisini azaltarak uyarlanabilir düzenleri basitleştirir ve boyutlar arasında geçiş yaparken yan etkilerin tetiklenmesini önler (pencerenin yeniden boyutlandırılması, yönü değiştirilmesi veya cihazın katlanması ve açılması nedeniyle ortaya çıkabilir).

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

@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

Oluştur'daki özel düzenler hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın.

Örnek uygulamalar

  • CanonicalLayouts, büyük ekranlı cihazlarda optimum kullanıcı deneyimi sunan kanıtlanmış tasarım kalıplarının bulunduğu bir depodur.
  • JetNews, kullanıcı arayüzünü mevcut alandan yararlanacak şekilde uyarlayan bir uygulamanın nasıl tasarlanacağını gösterir
  • Yanıtla, mobil cihazları, tabletleri 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