Jetpack Compose aşamaları

Diğer kullanıcı arayüzü araç setlerinin çoğunda olduğu gibi, Compose'da da birden fazla farklı aşamalar bulunmaktadır. Android View sistemine bakacak olursak, üç temel aşamalarından oluşur: ölçme, düzenleme ve çizim. Oluşturma çok benzer ancak bileşim adı verilen önemli bir ek aşamayı oluşturur.

Beste, Thinking in Compose ve State ve Jetpack Compose.

Bir karenin üç aşaması

Oluşturmanın üç ana aşaması vardır:

  1. Beste: Hangi kullanıcı arayüzü gösterilecek? Compose, composable işlevleri çalıştırır ve kullanıcı arayüzünüzle ilgili bir açıklama oluşturur.
  2. Düzen: Kullanıcı arayüzünün yerleştirileceği yer. Bu aşama iki adımdan oluşur: ölçüm ve yerleşim. Düzen öğeleri kendilerini ölçer, yerleştirir ve her düğüm için 2D koordinatlardaki alt öğeleri görebilirsiniz.
  3. Çizim: Nasıl oluşturulur? Kullanıcı arayüzü öğeleri bir Tuvale, genellikle cihaz ekranına gidin.
ziyaret edin.
Compose'un verileri kullanıcı arayüzüne dönüştürdüğü üç aşamanın (sırayla veri, kompozisyon, düzen, çizim, kullanıcı arayüzü) resmi.
Şekil 1. Compose'un verileri kullanıcı arayüzüne dönüştürdüğü üç aşamada.

Bu aşamaların sırası genellikle aynıdır ve verilerin tek bir aşamada akışına izin verir. bir kare üretmek için bileşimden düzene ve çizime kadar her yönüyle tek yönlü veri akışı olarak ayarlayın. BoxWithConstraints ve LazyColumn ve LazyRow önemli noktalar üst öğelerinin bileşimi üst yayıncının düzenine bağlı olan istisnalar aşamasındayız.

Bu üç aşamanın her kare için sanal olarak gerçekleştiğini varsayabilirsiniz. Ancak daha iyi bir performans elde etmek için Compose, tüm bu aşamalarda aynı girişlerden aynı sonuçları hesaplamalısınız. Oluştur atlayabilirsiniz. işlevini çağırıyorsa ve Oluştur kullanıcı arayüzü yeniden düzen vermiyorsa veya ağacın tamamını yeniden çizebilirsiniz. Oluşturma işlemi yalnızca kullanıcı arayüzünü güncellemek için gereken minimum çalışma miktarıdır. Bu optimizasyon, çünkü Oluştur, farklı aşamalardaki durum okumalarını izler.

Aşamaları anlama

Bu bölümde, composable'lar için üç Compose aşamasının nasıl yürütüldüğü açıklanmaktadır ayrıntılı olarak inceleyelim.

Beste

Beste aşamasında, Compose çalışma zamanı composable işlevleri yürütür kullanıcı arayüzünüzü temsil eden bir ağaç yapısı gösterir. Bu kullanıcı arayüzü ağacı şunları içerir: sonraki aşamalar için gereken tüm bilgileri içeren düzen düğümlerini aşağıdaki videoda gösteriliyor:

Şekil 2. Bestede oluşturulan kullanıcı arayüzünü temsil eden ağaç aşamasındayız.

Kod ve kullanıcı arayüzü ağacının alt bölümü aşağıdaki gibi görünür:

Beş composable'ı içeren kod snippet'i ve ortaya çıkan kullanıcı arayüzü ağacı, alt düğümlerin üst düğümlerinden dallandığı gibi.
Şekil 3. Kullanıcı arayüzü ağacının ilgili kodu içeren alt bölümü.

Bu örneklerde, koddaki her composable işlevi tek bir düzene eşlenir kullanıcı arayüzü ağacında. Daha karmaşık örneklerde composable'lar mantık ve ve farklı durumlara göre farklı bir ağaç üretmeyi öğreneceksiniz.

Düzen

Compose, düzen aşamasında beste aşamasında oluşturulan kullanıcı arayüzü ağacını kullanır giriş olarak kullanabilirsiniz. Düzen düğümleri koleksiyonu, 2D uzayda her düğümün boyutuna ve konumuna karar verme.

4.Şekil Düzen aşamasında, kullanıcı arayüzü ağacındaki her bir düzen düğümünün ölçümü ve yerleşimi.

Düzen aşamasında, ağaçta aşağıdaki üç adım kullanılarak geçilir algoritma:

  1. Alt öğeleri ölç: Bir düğüm, varsa alt öğelerini ölçer.
  2. Kendi boyutuna karar verme: Bu ölçümlere göre düğüm kendi başına karar verir. seçin.
  3. Alt öğeleri yerleştir: Her alt düğüm, bir düğümün kendi öğesine göre yerleştirilir. dokunun.

Bu aşamanın sonunda, her düzen düğümü:

  • Atanan width ve height
  • Çizilmesi gereken x, y koordinatı

Önceki bölümde verilen kullanıcı arayüzü ağacını geri çağırın:

Beş composable'ı içeren bir kod snippet'i ve ortaya çıkan kullanıcı arayüzü ağacı, alt düğümlerin üst düğümlerinden dallara ayrıldığı

Bu ağaç için algoritma aşağıdaki gibi çalışır:

  1. Row, Image ve Column olmak üzere alt öğelerini ölçer.
  2. Image ölçülür. Hiç çocuğu yok, bu yüzden kendi kararını verir. ve boyutu Row özelliğine geri bildirir.
  3. Ardından Column ölçülür. Kendi alt öğelerini ölçer (iki Text composables) emin olun.
  4. İlk Text ölçülmüştür. Hiç çocuğu yok, bu yüzden ve boyutunu Column değerine geri bildirir.
    1. İkinci Text ölçülür. Hiç çocuğu yok, bu yüzden ve Column parametresine geri bildirir.
  5. Column, kendi boyutuna karar vermek için alt ölçümleri kullanır. Şunu kullanır: alt öğelerinin yüksekliğinin toplamıdır.
  6. Column, çocuklarını kendisiyle göreli olarak yerleştirir ve birbirlerine bağladık.
  7. Row, kendi boyutuna karar vermek için alt ölçümleri kullanır. Şunu kullanır: maksimum alt öğe yüksekliği ve alt öğelerinin genişliklerinin toplamı. Bu durumda yardımcı olur.

Her düğümün yalnızca bir kez ziyaret edildiğini unutmayın. Compose çalışma zamanı için yalnızca bir tüm düğümleri ölçmek ve yerleştirmek için kullanıcı arayüzü ağacından geçmek, bu da bazı yolları da görmüştük. Ağaçtaki düğüm sayısı arttıkça harcanan zaman doğrusal bir şekilde artacaktır. Bunun aksine her bir düğüm ziyaret süresi katlanarak artar.

Çizim

Çizim aşamasında, ağaç tekrar yukarıdan aşağıya doğru çaprazlanır ve her bir çizim önce kendisini ekranda çizer.

5. Şekil. Çizim aşaması, pikselleri ekranda çizer.

Önceki örnekte, ağaç içeriği aşağıdaki şekilde çizilir:

  1. Row, arka plan rengi gibi olası içeriği çizer.
  2. Image kendini çeker.
  3. Column kendini çeker.
  4. Sırasıyla birinci ve ikinci Text, kendilerini çizer.

6. Şekil. Kullanıcı arayüzü ağacı ve çizilmiş temsili.

Durum okumaları

Bir sırasında anlık görüntü durumunun değerini okuduğunuzda sıralandığı aşamalardan birini izlerseniz, Compose'da ilk olarak değer okundu. Bu izleme, aşağıdaki durumlarda Compose'un okuyucuyu yeniden çalıştırmasına olanak tanır: durum değeri değişir ve Compose'daki durum gözlemlenebilirliğinin temelini oluşturur.

Durum, genellikle mutableStateOf() kullanılarak oluşturulur ve daha sonra bir üzerinden erişilir iki yöntem vardır: value mülküne doğrudan erişerek veya yeni bir kod oluşturabilirsiniz. Bunlar hakkında daha fazla bilgiyi Şurada bulabilirsiniz: composables olarak da bilinir. Şu amaçlara yönelik olarak: bir "eyalet okuma durumu" söz konusu eş değer erişimlerden herhangi birini yöntemlerine göz atın.

// State read without property delegate.
val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(paddingState.value)
)

// State read with property delegate.
var padding: Dp by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(padding)
)

Mülkün gelişmiş özellikleri yetki verin, "alıcı" ve "setter" fonksiyonları Eyaletin verilerine erişmek ve bunları güncellemek için value. Bu alıcı ve belirleyici işlevleri yalnızca özelliği oluşturulduğunda değil, değer olarak göstermektir; bu nedenle, yukarıdakiler eşdeğerdir.

Okuma durumu değiştiğinde yeniden yürütülebilecek her bir kod bloğu yeniden başlatma kapsamına girer. Oluşturma, durum değeri değişikliklerini takip eder ve yeniden başlatır farklı aşamalarda durumlarla karşılaşabilirsiniz.

Aşamalı durum okumaları

Yukarıda belirtildiği gibi, Oluşturma ve Oluşturma parçalarında üç ana aşama bulunur hangi durumun okunduğunu görebiliriz. Bu işlem, Compose'un yalnızca projenizdeki her unsur için gereken çalışmaları yapması gereken Kullanıcı arayüzü.

Her bir aşamayı ele alalım ve bir durum değeri okunduğunda ne olduğunu açıklayalım. bir bilgidir.

1. Aşama: Beste

@Composable işlevi veya lambda bloğu içindeki durum okumaları bileşimi etkiler sonraki aşamaları da içerebilir. Durum değeri değiştiğinde, recomposer programı, durum değeri. Çalışma zamanının, bu reklamların bazılarını ya da tamamını atlamasına composable işlevlerini kullanabilirsiniz. Girişler varsa atlama değişmedi bölümünü ziyaret edin.

Oluşturma kullanıcı arayüzü, kompozisyonun sonucuna bağlı olarak düzeni ve çizimi çalıştırır. aşamalar. İçerik aynı ve boyut aynı kalırsa bu aşamaları atlayabilir. ve düzen de değişmez.

var padding by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    // The `padding` state is read in the composition phase
    // when the modifier is constructed.
    // Changes in `padding` will invoke recomposition.
    modifier = Modifier.padding(padding)
)

2. Aşama: Düzen

Düzen aşaması iki adımdan oluşur: ölçüm ve yerleşim. İlgili içeriği oluşturmak için kullanılan ölçüm adımı, Layout composable'a iletilen ölçü lambda'yı çalıştırır. LayoutModifier arayüzünün MeasureScope.measure yöntemi vb. İlgili içeriği oluşturmak için kullanılan yerleşim adımı, layout işlevinin yerleşim bloğunu (lambda) çalıştırır. Modifier.offset { … } bloğu vb.

Bu adımların her biri sırasındaki durum okumaları, düzeni ve potansiyel olarak aşamasındayız. Durum değeri değiştiğinde Compose kullanıcı arayüzü, düzeni programlar aşamasındayız. Ayrıca boyut veya konum değişirse çizim aşamasını da çalıştırır.

Daha kesin konuşmak gerekirse ölçüm adımı ile yerleşim kapsamları yeniden başlatma: yerleşim adımındaki durum okumaları yeniden çağrılmaz adım adım ilerleyelim. Ancak bu iki adım çoğu zaman iç içe geçmiş olduğundan, yerleşim adımında okunan bir durum diğer yeniden başlatmaları etkileyebilir kapsamlarını belirleyebilirsiniz.

var offsetX by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.offset {
        // The `offsetX` state is read in the placement step
        // of the layout phase when the offset is calculated.
        // Changes in `offsetX` restart the layout.
        IntOffset(offsetX.roundToPx(), 0)
    }
)

3. Aşama: Çizim

Çizim kodu sırasındaki durum okumaları çizim aşamasını etkiler. Yaygın örnekler Canvas(), Modifier.drawBehind ve Modifier.drawWithContent dahil. Zaman durum değeri değiştiğinde, Oluştur kullanıcı arayüzü yalnızca çizim aşamasını çalıştırır.

var color by remember { mutableStateOf(Color.Red) }
Canvas(modifier = modifier) {
    // The `color` state is read in the drawing phase
    // when the canvas is rendered.
    // Changes in `color` restart the drawing.
    drawRect(color)
}

Durum okumalarını optimize etme

Compose, yerelleştirilmiş durum okuma izlemesi yaptığında, gerçekleştirilen çalışmadan elde edilen gelir ve çalışma şeklinin her durumu uygun aşamada okunarak.

Bir örnekle açıklayalım. Burada, ofset kullanan bir Image() değiştiricisi, son düzen konumunu dengelemek ve böylece Kullanıcı sayfayı kaydırır.

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        // Non-optimal implementation!
        Modifier.offset(
            with(LocalDensity.current) {
                // State read of firstVisibleItemScrollOffset in composition
                (listState.firstVisibleItemScrollOffset / 2).toDp()
            }
        )
    )

    LazyColumn(state = listState) {
        // ...
    }
}

Bu kod çalışır ancak optimum olmayan performansa neden olur. Yazıldığı gibi, kod firstVisibleItemScrollOffset durumunun değerini okur ve bunu "the" Modifier.offset(offset: Dp) işlevini kullanın. Kullanıcı sayfayı kaydırdıkça firstVisibleItemScrollOffset değeri unutmayın. Bildiğimiz gibi Compose, yeniden başlatılabilmesi için tüm durum okumalarını izler okuma kodunu (yeniden çağırın) tıklayın. Bu örnekteki değerimiz, Box

Bu, beste aşamasında okunan bir durum örneğidir. Bu kesinlikle kötü bir şey değil. Aslında yeniden düzenlemenin temelini oluşturur. veri değişikliklerinin yeni kullanıcı arayüzü yayınlamasına izin verir.

Bu örnekte optimum değildir, çünkü her kaydırma etkinliği yeniden değerlendiriliyor ve ardından ölçülmesi gerekiyor. zemini hazırladık. Her kaydırmada Oluşturma aşamasını tetikliyoruz Gösterilen ne değişmemiş olsa da yalnızca nerede gösterildiğine bağlıdır. Durum okuma işlemini yalnızca düzen aşamasını yeniden tetikleyecek şekilde optimize edebiliriz.

Ofset değiştiricinin başka bir sürümü de mevcuttur: Modifier.offset(offset: Density.() -> IntOffset).

Bu sürüm bir lambda parametresi alır; burada elde edilen ofset lambda bloğunu. Kullanılacak kodumuzu güncelleyelim:

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        Modifier.offset {
            // State read of firstVisibleItemScrollOffset in Layout
            IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2)
        }
    )

    LazyColumn(state = listState) {
        // ...
    }
}

Peki, bu neden daha iyi performans gösteriyor? Değiştiriciye sağladığımız lambda bloğu düzen aşamasında (özellikle de yerleşim aşaması sırasında yerleşim adımı), yani firstVisibleItemScrollOffset durumumuz daha uzun okumalarını sağlar. E-posta, durum okunduğunda izleme yaptığı için bu değişiklik, firstVisibleItemScrollOffset değeri değişirse Oluşturmak için düzen ve çizim aşamalarının yeniden başlatılması yeterlidir.

Bu örnek, ofset parametresini optimize edebilmek için farklı ofset değiştiricilerine bir sonuç elde edersiniz, ancak genel fikir doğrudur: Durum okumalarını mümkün olan en düşük aşamayı, yani Compose'un mümkün olan en az miktarda iş yeri.

Elbette, çoğu zaman bestedeki durumları okumak aşamasındayız. Yine de, aramızdaki iletişimin sayısını en aza yeniden bestelemenize olanak tanır. Bununla ilgili daha fazla bilgi için bakın derivedStateOf: bir veya birden fazla durum nesnesini diğerine dönüştürme durum.

Yeniden oluşturma döngüsü (döngüsel aşama bağımlılığı)

Daha önce, Compose'un aşamalarının her zaman aynı emin olmanız ve aynı karede geri gitmek mümkün değildir. Ancak bu, uygulamaların beste döngülerine dahil edilmesini yasaklamaz farklı karelerde kullanılabilir. Aşağıdaki örneğe bakın:

Box {
    var imageHeightPx by remember { mutableStateOf(0) }

    Image(
        painter = painterResource(R.drawable.rectangle),
        contentDescription = "I'm above the text",
        modifier = Modifier
            .fillMaxWidth()
            .onSizeChanged { size ->
                // Don't do this
                imageHeightPx = size.height
            }
    )

    Text(
        text = "I'm below the image",
        modifier = Modifier.padding(
            top = with(LocalDensity.current) { imageHeightPx.toDp() }
        )
    )
}

Burada, resmin en üstte olduğu dikey bir sütun uyguladık (kötü bir şekilde) altındaki metin de görünür. Şunları bilmek için Modifier.onSizeChanged() kullanıyoruz: ve ardından çözüm olarak metinde Modifier.padding() kullanarak aşağı kaydırın. Px ile Dp tarihleri arasındaki yapay dönüşüm zaten kodda bir sorun olduğunu gösterir.

Bu örnekteki sorun, nihai sonuca ulaşmadığımız düzen içinde olduğunu unutmayın. Kod, gerçekleşen birden fazla kareye dayanır ve bu kareler kullanıcı arayüzü, kullanıcı için ekranda sürekli olarak atlanır.

Neler olduğunu görmek için her bir kareyi tek tek inceleyelim:

İlk karenin beste aşamasında imageHeightPx değeri 0, ve metin Modifier.padding(top = 0) ile sağlanır. Ardından, düzen aşaması devam eder ve onSizeChanged değiştiricisi için geri çağırma çağrılır. Bu işlemde imageHeightPx, resmin gerçek yüksekliğine güncellenir. Sonraki kare için yeniden besteleme programları oluşturun. Çizim aşamasında, değer değişikliği yansıtılmadığından metin, 0 dolgusuyla oluşturulur (henüz).

Oluştur, ardından şu değer değişikliğine göre programlanan ikinci kareyi başlatır: imageHeightPx Durum, Box içerik bloğunda okunur ve çağrılır aşamasına geçelim. Bu kez metin bir dolguyla sağlanır resim yüksekliğiyle eşleşiyor. Düzen aşamasında bu kod, Tekrar imageHeightPx, ancak değerden bu yana yeniden düzenleme planlanmadı aynı kalır.

Sonunda metinde istenen dolguyu alırız ancak bu dolgunun dolgu değerini farklı bir aşamaya geri aktarmak için fazladan bir kare harcamasını çakışan içeriğe sahip bir kare oluşturulmasıyla sonuçlanır.

Bu örnek biraz çelişkili görünebilir, ancak şu genel örüntüye dikkat edin:

  • Modifier.onSizeChanged(), onGloballyPositioned() veya başka bir düzen işlemler
  • Bir eyaleti güncelle
  • Bu durumu düzen değiştiricide giriş olarak kullan (padding(), height() veya benzer)
  • Tekrarlama ihtimali var

Yukarıdaki örneğin çözümü, uygun düzen temel öğelerini kullanmaktır. Örnek yukarıdaki basit bir Column() ile uygulanabilir, ancak daha fazla özel bir işlem gerektiren karmaşık bir örnek teşkil ediyor. Bu da özel düzen. Özel düzenler kılavuzunu inceleyin konulu videomuzu izleyin.

Buradaki genel ilke, birden fazla kullanıcı arayüzü için tek bir doğruluk kaynağına sahip olmaktır ölçülmesi ve yerleştirilmesi gereken unsurlardır. Kullanım bir düzen temel öğesi oluşturmak veya özel bir düzen oluşturmak, ortak ebeveyn, ilişki ve korelasyon arasındaki ilişkiyi koordine eden bilgi kaynağı işlevi görür ekleyebilirsiniz. Dinamik bir durumun uygulanması bu ilkeyi çiğnemektedir.

ziyaret edin.