Uyarlanabilir düzenler oluşturma

Uygulamanızın kullanıcı arayüzü farklı ekran boyutlarını, yönleri ve form faktörlerini hesaba katacak şekilde duyarlı olmalıdır. Uyarlanabilir düzen, kullanılabilir ekran alanına göre değişir. Bu değişiklikler, basit düzen ayarlamalarından yer doldurmaya ve ek odadan yararlanmak için düzenleri tamamen değiştirmeye kadar değişiklik gösterir.

Bildirim temelli bir kullanıcı arayüzü araç seti olan Jetpack Compose, farklı boyutlarda içerik oluşturmak için kendisini ayarlayan düzenler tasarlamak ve uygulamak için çok uygundur. Bu dokümanda, kullanıcı arayüzünüzü duyarlı hale getirmek için Oluştur'u nasıl kullanabileceğinizle ilgili bazı yönergeler bulunmaktadır.

Ekran düzeyinde composable'lar için büyük düzen değişikliklerinin uygunsuz olmasını sağlayın

Bir uygulamanın tamamını yerleştirmek için Compose'u kullanırken uygulamanızın oluşturulması için verilen tüm alanı uygulama düzeyinde ve ekran düzeyinde composable'lar 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 verirken fiziksel, donanım değerleri kullanmaktan kaçının. Kararları 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 soruların yanıtları, kullanıcı arayüzünüzün çalışabileceği alanı belirlemek için faydalı olmayabilir.

Telefon, katlanabilir cihaz, tablet ve dizüstü bilgisayar gibi çeşitli cihaz form faktörlerini gösteren şema

Tabletlerde bir uygulama çoklu pencere modunda çalışıyor olabilir. Bu da, uygulamanın ekranı başka bir uygulamaya böldüğü anlamına gelir. ChromeOS'te bir uygulama, yeniden boyutlandırılabilir bir pencerede olabilir. Hatta katlanabilir cihaz gibi birden fazla fiziksel ekran bile olabilir. Bunların tümünde, fiziksel ekran boyutu içeriğin nasıl görüntüleneceğine karar verirken dikkate alınmaz.

Bunun yerine, Jetpack WindowManager kitaplığı tarafından sağlanan mevcut pencere metrikleri gibi ekranın uygulamanıza ayrılan gerçek bölümüne göre kararlar almanız gerekir. Bir Compose uygulamasında WindowManager'ın nasıl kullanılacağını görmek için JetNews örneğine göz atın.

Bu yaklaşımı uygulamak, uygulamanızı yukarıdaki senaryoların tümünde iyi davranacağı için daha esnek hale getirir. Düzenlerinizi ekran alanına uygun hale getirmek, ChromeOS gibi platformları ve tabletler ile katlanabilir cihazlar gibi form faktörlerini desteklemek için gereken özel kullanım miktarını da azaltır.

Uygulamanız için kullanılabilir olan ilgili alanı gözlemledikten sonra, Pencere Boyutu Sınıfları bölümünde açıklandığı gibi ham boyutu anlamlı bir boyut sınıfına dönüştürmek faydalıdır. Bu işlem, boyutları standart boyut grupları halinde gruplandırır. Bunlar, basitlik ile uygulamanızı benzersiz durumlar 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 aktarabilir veya iç içe yerleştirilmiş composable'lara geçirmek üzere türetilmiş durum oluşturmak için ek mantık uygulayabilirsiniz.

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val windowSizeClass = calculateWindowSizeClass(this)
            MyApp(windowSizeClass)
        }
    }
}
@Composable
fun MyApp(windowSizeClass: WindowSizeClass) {
    // Perform logic on the size class to decide whether to show
    // the top app bar.
    val showTopAppBar = windowSizeClass.heightSizeClass != 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ız genelinde senkronize edilmesi gereken birçok yere dağıtmak yerine tek bir konumla sınırlar. Bu tek konum, durumu oluşturur. Bu durum, diğer uygulama durumlarında olduğu gibi, diğer composable'lara açıkça geçirilebilir. Durumun açıkça iletilmesi, tek tek composable'ları basitleştirir. Çünkü bunlar, diğer verilerle birlikte boyut sınıfını veya belirtilen yapılandırmayı alan normal composable işlevler olacaktır.

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

Composable'lar çok çeşitli yerlere yerleştirildiklerinde daha fazla tekrar kullanılabilir durumdadır. Bir composable, her zaman belirli bir boyutla belirli bir konuma yerleştirileceğini varsayarsa onu farklı bir konumda ya da farklı miktarda boş alanla başka bir yerde yeniden kullanmak daha zor olur. Bu aynı zamanda, bağımsız, yeniden kullanılabilir composable'ların "genel" boyut bilgilerine dolaylı olarak bağlı kalmaması gerektiği anlamına gelir.

Bir örneği inceleyelim: Yan yana bir bölme veya iki bölme gösterebilen, list-detail düzeni uygulayan iç içe yerleştirilmiş bir composable.

İki bölmeyi yan yana gösteren bir uygulamanın ekran görüntüsü

Şekil 1. Tipik bir liste/ayrıntı düzenini gösteren bir uygulamanın ekran görüntüsü. 1 liste alanını, 2 ise ayrıntı alanını ifade eder.

Bu kararın, uygulamanın genel düzeninin bir parçası olmasını istiyoruz. Bu nedenle, yukarıda gördüğümüz gibi ekran düzeyinde bir composable'a ait kararı iletiyoruz:

@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 bazı boyutlara göre bazı mantık gerçekleştirmek istiyoruz ama özellikle hangi boyutu temel alırsak?

İki farklı karta örnekler: Yalnızca simge ile başlığı gösteren dar bir kart ve 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 boyutunu kullanmaya çalışmaktan kaçınmamız gerekir. Bu durum, birden fazla ekran için doğru olmadığı gibi uygulama tam ekran değilse doğru olmaz.

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

Bu nedenle, composable'ın aslında kendisini oluşturmak için verildiği genişliği kullanmalıyız. Bu genişliği elde etmek için iki seçeneğimiz vardır:

İçeriğin nerede veya nasıl görüntüleneceğini değiştirmek isterseniz, düzeni duyarlı hale getirmek için bir değiştiriciler koleksiyonu veya özel düzen kullanabilirsiniz. Bu, mevcut tüm alanı küçük bir çocuğun doldurması veya yeterli alan varsa çocukları birden fazla sütuna yerleştirmek gibi basit bir yaklaşım olabilir.

Gösterdiğiniz öğeyi değiştirmek istiyorsanız, daha güçlü bir alternatif olarak BoxWithConstraints kullanabilirsiniz. Bu composable, kullanılabilir alana göre farklı composable'ları çağırmak için kullanabileceğiniz ölçüm kısıtlamaları sağlar. Bununla birlikte, BoxWithConstraints bu kısıtlamaların bilindiği Düzen aşamasına kadar bileşimi ertelediğinden, yerleşim sırasında daha fazla iş yapılmasına neden olur. Bu nedenle, bunun bir maliyeti vardır.

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

Tüm verilerin farklı boyutlar için kullanılabildiğinden emin olun

Ek ekran alanından yararlanırken, büyük bir ekranda kullanıcıya küçük bir ekrana göre daha fazla içerik gösterebilirsiniz. Bu davranışa sahip bir composable'ı uygularken verim almak ve verileri mevcut boyutun yan etkisi olarak yüklemek cazip gelebilir.

Ancak bu, verilerin toplanıp uygun bir şekilde oluşturulması için composable'lara sağlandığı tek yönlü veri akışı ilkelerine aykırıdır. Verilerin bir kısmı her zaman kullanılmasa bile composable'ın her zaman tüm boyutlarda göstermesi gereken verilere sahip olması için composable'a 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ğine dayanarak, description öğesinin her zaman Card öğesine iletildiğini unutmayın. description yalnızca genişlik gösterilmesine izin verdiğinde kullanılsa da Card, kullanılabilir genişlikten bağımsız olarak her zaman için gereklidir.

Her zaman iletilen veriler, uyarlanabilir düzenleri daha az durum bilgili hale getirerek daha basit hale getirir ve boyutlar arasında geçiş yaparken yan etkilerin tetiklenmesini önler (pencere yeniden boyutlandırma, yön değişikliği veya cihazın katlanıp genişletilmesi nedeniyle olabilir).

Bu ilke aynı zamanda düzen değişikliklerinde durumun korunmasına da olanak tanır. Tüm boyutlarda kullanılmayabilecek bilgileri kaldırarak düzen boyutu değiştiğinde kullanıcının durumunu koruyabiliriz. Örneğin, yeniden boyutlandırmalar düzenin gizleme ve açıklamayı gösterme arasında geçiş yapmasına neden olduğunda kullanıcının durumunun korunması için bir 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 başvurun.

Örnek uygulamalar

  • Büyük ekran standart düzenleri, büyük ekranlı cihazlarda optimum kullanıcı deneyimi sağlayan, etkisi kanıtlanmış tasarım kalıplarından oluşan bir depodur.
  • JetNews, kullanıcı arayüzünü mevcut alandan yararlanacak şekilde uyarlayan bir uygulamanın nasıl tasarlanacağını gösteriyor
  • Reply mobil cihazlar, tabletler ve katlanabilir cihazları desteklemek için uyarlanabilir bir örnektir
  • Artık Android'de farklı ekran boyutlarını desteklemek için uyarlanabilir düzenler kullanan bir uygulama

Videolar