Compose düzenlerindeki içsel ölçümler

Compose'un kurallarından biri de alt öğelerinizi yalnızca bir kez ölçmenizdir. Alt öğeleri iki kez ölçmek, çalışma zamanı istisnasına neden olur. Bununla birlikte, ölçümden önce çocuklarınızla ilgili bazı bilgilere ihtiyaç duyduğunuz zamanlar vardır.

Intrinsics, çocukları ölçülmeden önce sorgulamanızı sağlar.

Bir composable'a intrinsicWidth veya intrinsicHeight vermesini isteyebilirsiniz:

  • (min|max)IntrinsicWidth: Bu yükseklik göz önüne alındığında, içeriğinizi doğru şekilde boyayabileceğiniz minimum/maksimum genişlik nedir?
  • (min|max)IntrinsicHeight: Bu genişliğe göre içeriğinizi doğru şekilde boyayabileceğiniz minimum/maksimum yükseklik nedir?

Örneğin, sonsuz width içeren bir Text öğesinin minIntrinsicHeight değerine bakarsanız metin tek bir satırda çizilmiş gibi Text height değerini döndürür.

Temel özelliklerin işleyiş şekli

Ekranda iki metni aşağıdaki gibi ayırıcıyla ayrılmış bir şekilde görüntüleyen bir composable oluşturmak istediğinizi varsayalım:

Aralarında dikey bir ayırıcı bulunan, yan yana iki metin öğesi

Bunu nasıl yapabiliriz? İçinde olabildiğince çok genişleyen iki Text ve ortasında bir Divider olan bir Row olabilir. Divider öğesinin yüksekliği en yüksek Text ve ince (width = 1.dp) olmasını istiyoruz.

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        Divider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

Bunu önizlediğimizde, Divider öğesinin tüm ekrana genişlediğini görürüz ve istediğimiz şey bu değildir:

İki metin öğesi, aralarında bir ayırıcı olan yan yana duruyor ancak ayırıcı, metnin alt kısmında aşağı doğru uzanıyor

Bunun nedeni, Row özelliğinin her bir alt öğeyi ayrı olarak ölçmesi ve Text yüksekliğinin Divider özelliğini sınırlandırmak için kullanılamamasıdır. Divider öğesinin, mevcut alanı belirli bir yükseklikle doldurmasını isteriz. Bunun için height(IntrinsicSize.Min) değiştiricisini kullanabiliriz .

height(IntrinsicSize.Min), çocuklarını doğal olarak en kısa boyda olmak zorunda kalacak şekilde boyutlandırır. Yinelemeli olduğundan Row ve alt öğelerini minIntrinsicHeight sorgulayacak.

Bu komut, kodumuza uygulandığında beklendiği gibi çalışacaktır:

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier.height(IntrinsicSize.Min)) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        Divider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

// @Preview
@Composable
fun TwoTextsPreview() {
    MaterialTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

Önizlemeyle:

Aralarında dikey bir ayırıcı bulunan, yan yana iki metin öğesi

Row composable'ın minIntrinsicHeight alt öğesi, alt öğeleri arasında maksimum minIntrinsicHeight olacak. Divider öğesinin minIntrinsicHeight değeri, hiçbir sınırlama verilmediğinde yer kaplamadığı için 0'dır. Text minIntrinsicHeight değeri, belirli bir width değeri verilen metnin değeri olur. Bu nedenle, Row öğesinin height kısıtlaması, Text değerlerinin maksimum minIntrinsicHeight değeri olur. Divider, daha sonra height öğesini Row tarafından belirtilen height kısıtlamasına genişletir.

Özel düzenlarınızdaki iç öğeler

Özel bir Layout veya layout değiştiricisi oluştururken doğal ölçümler yaklaşık değerlere göre otomatik olarak hesaplanır. Bu nedenle, hesaplamalar tüm düzenler için doğru olmayabilir. Bu API'ler, bu varsayılanları geçersiz kılma seçenekleri sunar.

Özel Layout öğenizin içsel ölçümlerini belirtmek için MeasurePolicy arayüzünü oluştururken minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth ve maxIntrinsicHeight öğelerini geçersiz kılın.

@Composable
fun MyCustomComposable(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        content = content,
        modifier = modifier,
        measurePolicy = object : MeasurePolicy {
            override fun MeasureScope.measure(
                measurables: List<Measurable>,
                constraints: Constraints
            ): MeasureResult {
                // Measure and layout here
                // ...
            }

            override fun IntrinsicMeasureScope.minIntrinsicWidth(
                measurables: List<IntrinsicMeasurable>,
                height: Int
            ): Int {
                // Logic here
                // ...
            }

            // Other intrinsics related methods have a default value,
            // you can override only the methods that you need.
        }
    )
}

Özel layout değiştiricinizi oluştururken LayoutModifier arayüzünde ilgili yöntemleri geçersiz kılın.

fun Modifier.myCustomModifier(/* ... */) = this then object : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // Measure and layout here
        // ...
    }

    override fun IntrinsicMeasureScope.minIntrinsicWidth(
        measurable: IntrinsicMeasurable,
        height: Int
    ): Int {
        // Logic here
        // ...
    }

    // Other intrinsics related methods have a default value,
    // you can override only the methods that you need.
}