TextMeasurer, metnin ekranda gerçekten oluşturulmasından önce boyutlarını ölçmek için tasarlanmış bir Jetpack Compose API’sidir. Android View’daki Paint.measureText aracılığıyla klasik yaklaşımın aksine, TextMeasurer stil ve kısıtlamaları dikkate alarak genişlik, yükseklik ve satır metriklerini elde etmek için Compose-native bir yol sağlar. Android Developers Documentation’a (2025) göre TextMeasurer, metin boyutunun diğer öğelerin konumlandırılmasını etkilediği özel Layout bileşenlerinde, animasyonlu etiketlerde ve uyarlanabilir UI öğelerinde aktif olarak kullanılmaktadır.
Ana Noktalar
TextMeasurer, androidx.compose.ui.text paketinden gelen işlevsel bir arayüz olup, Compose’da metni ekranda gerçekten oluşturmadan ölçmeye olanak tanır. Compose metin motorunun bir parçasıdır ve işleme için Skia kullanan Paragraph API düzeyinde çalışır.
TextMeasurer’ın temel amacı ön ölçümdür. Compose’da metin boyutu genellikle Composable Layout’a yerleştirildikten sonra belirlenir. Ancak, düzenlemeden önce metin genişliğini bilmeniz gereken senaryolar vardır — örneğin, bir dizenin bir kaba sığıp sığmadığına karar vermek veya metin uzunluğuna göre bir düğmenin boyutunu hesaplamak için.
TextMeasurer, bir TextMeasureRequest (metin, stil, kısıtlamalar) alan ve TextLayoutResult (metrikler, satırlar, karakterler) döndüren measure() metodunu sağlayarak bunu çözer. Bu, geliştiricinin tüm metin verilerini tek kare gecikme olmadan eşzamanlı olarak almasına olanak tanır.
Metin boyutunun ana bileşen boyutunu etkilediği durumlarda, animasyonlu metin geçişleri oluştururken veya metnin diğer öğelerle bir arada bulunduğu özel Layout’lar uygularken TextMeasurer’ı kullanın.
TextMeasurer API’si iki temel varlıktan oluşur: rememberTextMeasurer() (örnek oluşturma) ve measure() (ölçümü çalıştırma). Örnek, bileşenin tüm yaşam döngüsü için bir kez oluşturulur ve tüm ölçümler için yeniden kullanılır.
val textMeasurer = rememberTextMeasurer()
val result = textMeasurer.measure(
text = "Hello, Compose!",
style = MaterialTheme.typography.bodyLarge,
constraints = Constraints(maxWidth = 200, maxHeight = 50)
)
// result.size.width, result.size.height, result.lineCount
constraints parametresi, metnin sığması gereken maksimum genişlik ve yüksekliği tanımlar. Metin genişliğe sığmazsa, bir sonraki satıra kaydırılır. Ölçüm sonucu, satır sayısını, her karakterin konumunu, her satırın genişliğini ve genel metin boyutunu sağlayan bir TextLayoutResult içerir.
TextMeasurer, UI iş parçacığında eşzamanlı olarak çalışır ve yeniden oluşturmayı tetiklemez. Ancak, farklı stillerle sık sık metin ölçümü yük oluşturabilir — bu nedenle örnek remember aracılığıyla oluşturulur ve yeniden kullanılır.
En basit senaryo, ana kabın boyutunu belirlemek için tek satırlık metnin genişliğini ölçmektir. Örneğin, genişliği etiketin uzunluğuna bağlı olan bir düğme için.
val textMeasurer = rememberTextMeasurer()
var label by remember { mutableStateOf("Submit") }
val textResult = remember(label) {
textMeasurer.measure(
text = label,
style = MaterialTheme.typography.labelLarge,
constraints = Constraints(maxWidth = 1000, maxHeight = 100)
)
}
Box(
modifier = Modifier
.width(textResult.size.width.dp)
.height(40.dp)
.background(Color.Blue)
.clickable { onClick() }
) {
Text(text = label, style = MaterialTheme.typography.labelLarge)
}
Bu örnekte, remember(label) ölçümün yalnızca metin değiştiğinde yeniden başlamasını sağlar. Satır kaydırmayı önlemek için kısıtlamalar büyük bir maxWidth (1000) ile ayarlanır — gerçek kısıtlama metin genişliğine eşit olacaktır. Box genişliği, ölçülen metin genişliğine ayarlanır ve “metne göre boyutlandırılmış düğme” efekti elde edilir.
Not: sonuçtaki genişlik .dp kullanılarak dp’ye dönüştürülmelidir, çünkü TextMeasurer boyutları piksel cinsinden döndürürken Modifier.width Dp bekler. Doğruluk için density ve LocalDensity.current kullanın.
Çok satırlı metin için TextMeasurer, yalnızca toplam yüksekliği değil, aynı zamanda satır sayısını ve paragraf içindeki her karakterin konumunu da belirlemenizi sağlar. Bu, özel metin alanları, sohbetler veya düzenleyiciler oluştururken gereklidir.
Çok satırlı metin için temel TextLayoutResult özellikleri: lineCount (satır sayısı), getLineTop(index) (satır başlangıcının Y koordinatı), getLineBottom(index) (satır sonunun Y koordinatı), getLineWidth(index) (satır genişliği). Bunları birleştirerek, arayüz öğelerini metin satırlarına göre hassas bir şekilde konumlandırabilirsiniz.
val textMeasurer = rememberTextMeasurer()
val result = textMeasurer.measure(
text = "Long multiline text that needs to be measured before layout",
style = MaterialTheme.typography.bodyMedium,
constraints = Constraints(maxWidth = 150, maxHeight = 300)
)
val linesCount = result.lineCount
val firstLineWidth = result.getLineWidth(0)
val totalHeight = result.size.height
getLineWidth(lineIndex) metodu, belirtilen satırın genişliğini piksel cinsinden döndürür. Satır kap sınırına ulaşmazsa maxWidth’den küçük olabilir. Her satırın genişliğini bilerek, manuel olarak yaslama efekti uygulayabilir veya kısa satırların yanına satır içi öğeler yerleştirebilirsiniz.
Belirli bir karakterin konumunu almak için getBoundingBox(offset) kullanın; bu, left, top, right, bottom koordinatlarına sahip bir Rect döndürür. Bu, özel metin alanlarında imleç konumlandırma veya bir metin aralığını animasyonla vurgulamak için kullanışlıdır.
TextMeasurer’ın en güçlü uygulaması, metnin diğer öğelerle bir arada bulunduğu ve boyutlarının birbirine bağlı olduğu özel Layout bileşenleridir. Örneğin, simgenin tüm blok yerine metnin ilk satırına göre ortalanması gereken bir “Simge + metin” widget’ı.
Standart bir Row’da simge, tüm Row yüksekliğine göre ortalanır; bu, metin 3 satır kaplarken simge tek satır olduğunda doğal görünmez. TextMeasurer ile ilk satırı ölçebilir ve simgeyi tam olarak ona hizalayabilirsiniz.
val textMeasurer = rememberTextMeasurer()
Layout(
content = {
Icon(imageVector = Icons.Default.Star, contentDescription = null)
Text(text = "Multi-line text here")
},
measurePolicy = { measurables, constraints ->
val textMeasurable = measurables[1]
val textPlaceable = textMeasurable.measure(constraints)
val iconPlaceable = measurables[0].measure(constraints)
val firstLineHeight = textMeasurer.measure(
text = "Sample",
style = MaterialTheme.typography.bodyMedium,
constraints = Constraints(maxWidth = 2000, maxHeight = 100)
).size.height
layout(width = textPlaceable.width + iconPlaceable.width,
height = maxOf(textPlaceable.height, iconPlaceable.height)) {
iconPlaceable.placeRelative(0, (firstLineHeight - iconPlaceable.height) / 2)
textPlaceable.placeRelative(iconPlaceable.width, 0)
}
}
)
Bu örnekte, TextMeasurer aracılığıyla ilk satırın ölçülmesi simgenin konumunu belirler. TextMeasurer olmadan simge, çok satırlı metnin tüm yüksekliğine göre ortalanır ki bu görsel olarak yanlıştır. Bu desen sohbetlerde, kişi listelerinde ve bilgi kartlarında yaygın olarak kullanılır.
TextMeasurer, UI iş parçacığında çalışan eşzamanlı bir API’dir. Aşırı kullanımı, özellikle zayıf cihazlarda büyük metin hacimlerini ölçerken kare düşüşlerine neden olabilir.
İlk kural — sonucu önbelleğe alın. measure() çağrısını her zaman metin ve stil bağımlılıklarıyla remember içine sarın. CompositionLocal içinde veya sıcak yeniden oluşturma yollarında asla measure() çağırmayın.
İkinci kural — maxWidth ve maxHeight’i sınırlayın. maxWidth = Constraints.Infinity olarak ayarlarsanız, TextMeasurer satırları kaydıramaz ve metin ekran dışına çıkar. Özellikle çok satırlı metin için her zaman gerçekçi kısıtlamalar girin.
Üçüncü kural — aynı parametrelerle tekrarlanan ölçümden kaçının. Metin ve stil değişmediyse, measure() sonucu aynı olacaktır. Zaman uyumsuz senaryolar için ölçümü Deferred veya LaunchedEffect ile birleştirin.
Sıkça Sorulan Sorular
TextMeasurer metni oluşturmadan önce ölçerken, onSizeChanged sonra ölçer. TextMeasurer, metin boyutu ana düzeni etkilediğinde gereklidir; onSizeChanged, oluşturma sonrası reaktif mantık için uygundur.
Evet, TextMeasurer.measure(), satır içi stiller, bağlantılar ve renkler içeren SpannableString’i içeren AnnotatedString’i kabul eder. Tüm aralıklar ölçüm sırasında dikkate alınır.
TextMeasurer, tüm boyutların piksel cinsinden olduğu Skia oluşturma düzeyinde çalışır. LocalDensity.current aracılığıyla dönüştürün: density.run { width.toDp() }.
TextMeasurer, özel bir yazı tipi yüklenene kadar FontFamily.Default kullanır. Doğru ölçüm için, FontResource aracılığıyla yazı tipinin yüklenmesini bekleyin ve async/await kullanın.
Her LazyColumn öğesinde metin ölçmek, zayıf cihazlarda FPS’yi düşürebilir. Optimizasyon: remember aracılığıyla sonucu önbelleğe alın ve metin boyutunu sınırlayın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun