TextMeasurer — ekranda faktiki render edilmədən əvvəl mətnin ölçülərini ölçmək üçün nəzərdə tutulmuş Jetpack Compose API-sidir. Android View-da Paint.measureText vasitəsilə klassik yanaşmadan fərqli olaraq, TextMeasurer üslub və məhdudiyyətləri nəzərə alaraq mətnin enini, hündürlüyünü və sətir metrikalarını əldə etmək üçün Compose-native üsul təqdim edir. Android Developers Documentation (2025) məlumatına görə, TextMeasurer fərdi Layout komponentlərində, animasiya edilmiş başlıqlarda və mətn ölçüsünün digər elementlərin yerləşdirilməsinə təsir etdiyi adaptiv UI elementlərində fəal şəkildə istifadə olunur.
Əsas məqamlar
TextMeasurer — ekranda birbaşa render etmədən Compose-da mətni ölçməyə imkan verən androidx.compose.ui.text paketindən funksional interfeysdir. O, Compose mətn mühərrikinin bir hissəsidir və Skia-dan render üçün istifadə edən Paragraph API səviyyəsində işləyir.
TextMeasurer-in əsas təyinatı ilkin ölçmədir. Compose-da mətnin ölçüsü adətən Composable artıq Layout-da yerləşdirildikdən sonra müəyyən edilir. Lakin bəzi ssenarilərdə mətnin enini kompozisiyadan əvvəl bilmək lazımdır: məsələn, sətrin konteynerə sığıb-sığmadığını müəyyən etmək və ya mətnin uzunluğuna əsasən düymənin ölçüsünü hesablamaq.
TextMeasurer bu problemi measure() metodu ilə həll edir. O, TextMeasureRequest (mətn, üslub, məhdudiyyətlər) qəbul edir və TextLayoutResult (metrikalar, sətirlər, simvollar) qaytarır. Bu, proqramçıya bir kadr gecikməsi olmadan mətn haqqında bütün məlumatları sinxron şəkildə əldə etməyə imkan verir.
TextMeasurer-dən mətn ölçüsünün valideyn komponentinin ölçüsünə təsir etdiyi hallarda, animasiya edilmiş mətn keçidləri yaradarkən və ya mətnin digər elementlərlə yanaş olduğu xüsusi Layout-ları tətbiq edərkən istifadə edin.
TextMeasurer API-si iki əsas varlıqdan ibarətdir: rememberTextMeasurer() (nümunənin yaradılması) və measure() (ölçmənin başladılması). Nümunə komponentin bütün həyat dövrü üçün bir dəfə yaradılır və bütün ölçmələr üçün təkrar istifadə olunur.
val textMeasurer = rememberTextMeasurer()
val result = textMeasurer.measure(
text = "Hello, Compose!",
style = MaterialTheme.typography.bodyLarge,
constraints = Constraints(maxWidth = 200, maxHeight = 50)
)
// nəticənin eni, nəticənin hündürlüyü, sətirlərin sayı
constraints parametri mətnin yerləşməli olduğu maksimum en və hündürlüyü müəyyən edir. Mətn enə sığmazsa, növbəti sətrə keçirilir. Ölçmə nəticəsində sətirlərin sayını, hər simvolun mövqeyini, hər sətrin enini və mətnin ümumi ölçüsünü əldə edə biləcəyiniz TextLayoutResult var.
TextMeasurer UI axınında sinxron işləyir və yenidən kompozisiyaya səbəb olmur. Bununla belə, müxtəlif üslublarla mətnin tez-tez ölçülməsi yük yarada bilər — buna görə də nümunə remember vasitəsilə yaradılır və təkrar istifadə olunur.
Ən sadə ssenari — valideyn konteynerinin ölçüsünü müəyyən etmək üçün təksətirli mətnin enini ölçməkdir. Məsələn, eni etiketin uzunluğundan asılı olan düymə üçün.
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 nümunədə remember(label) ölçmənin yalnız mətn dəyişdikdə yenidən başlamasına zəmanət verir. Constraints sətirin keçirilməsinin qarşısını almaq üçün böyük maxWidth (1000) ilə təyin edilmişdir — faktiki məhdudiyyət mətnin eninə bərabər olacaqdır. Box-ın eni ölçülmüş mətn eninə təyin edilir ki, bu da „mətn ölçüsünə uyğun düymə" effekti verir.
Diqqət: nəticədən eni .dp vasitəsilə dp-ə çevirmək lazımdır, çünki TextMeasurer piksel olaraq ölçüləri qaytarır, Modifier.width isə Dp gözləyir. Dəqiqlik üçün density və LocalDensity.current istifadə edin.
Çoxsətirli mətn üçün TextMeasurer yalnız ümumi hündürlüyü deyil, həm də sətirlərin sayını və hər simvolun mövqeyini abzas daxilində müəyyən etməyə imkan verir. Bu, xüsusi mətn sahələri, çatlar və ya redaktorlar yaradarkən lazımdır.
Çoxsətirli mətn üçün TextLayoutResult-un əsas xüsusiyyətləri: lineCount (sətirlərin sayı), getLineTop(index) (sətrin başlanğıcının Y koordinatı), getLineBottom(index) (sonun Y koordinatı), getLineWidth(index) (sətrin eni). Onları birləşdirərək interfeys elementlərini mətn sətirlərinə nisbətən dəqiq yerləşdirə bilərsiniz.
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 göstərilən sətrin enini piksel olaraq qaytarır. Sətir konteynerin sərhədinə çatmazsa, bu maxWidth-dən az ola bilər. Hər sətrin enini bilərək, justifikasiya effektini əl ilə həyata keçirə və ya qısa sətirlərin yanında inline elementlər yerləşdirə bilərsiniz.
Müəyyən bir simvolun mövqeyini əldə etmək üçün left, top, right, bottom koordinatları ilə Rect qaytaran getBoundingBox(offset) istifadə edin. Bu, xüsusi mətn sahələrində kursorun yerləşdirilməsi və ya animasiya ilə mətn diapazonunun vurğulanması üçün faydalıdır.
TextMeasurer-in ən güclü tətbiq ssenarisi — mətnin digər elementlərlə yanaşı olduğu və ölçülərinin qarşılıqlı asılı olduğu xüsusi Layout komponentləridir. Məsələn, "İkon + mətn" vidjeti, burada ikon bütün bloka deyil, mətnin ilk sətirinə nisbətən mərkəzləşdirilməlidir.
Standart Row-da ikon bütün Row-un hündürlüyünə görə mərkəzləşdirilir ki, bu da mətn 3 sətir, ikon isə bir sətir tutduqda təbii görünmür. TextMeasurer ilə ilk sətri ölçmək və ikonu məhz ona nisbətən düzləndirmək olar.
val textMeasurer = rememberTextMeasurer()
Layout(
content = {
Icon(imageVector = Icons.Default.Star, contentDescription = null)
Text(text = "Burada çoxsətirli mətn")
},
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 nümunədə TextMeasurer vasitəsilə ilk sətrin ölçülməsi ikonun mövqeyini müəyyən edir. TextMeasurer olmadan ikon çoxsətirli mətnin bütün hündürlüyünə görə mərkəzləşərdi ki, bu da vizual olaraq düzgün deyil. Bu nümunə çatlarda, kontakt siyahılarında və məlumat kartlarında geniş istifadə olunur.
TextMeasurer — UI axınında işləyən sinxron API-dir. Həddindən artıq istifadə edildikdə, xüsusilə zəif cihazlarda böyük həcmdə mətn ölçərkən kadr itkisinə səbəb ola bilər.
Birinci qayda — nəticəni keşləyin. Həmişə measure() çağırışını mətn və üslubdan asılılıqlarla remember-ə sarın. Heç vaxt measure()-i CompositionLocal daxilində və ya recomposition-un istiqamətlərində çağırmayın.
İkinci qayda — maxWidth və maxHeight-i məhdudlaşdırın. maxWidth = Constraints.Infinity təyin etsəniz, TextMeasurer sətirləri keçirə bilməyəcək və mətn ekrandan kənara çıxacaq. Həmişə realistik məhdudiyyətlər təyin edin, xüsusilə çoxsətirli mətn üçün.
Üçüncü — eyni parametrlərlə təkrar ölçmədən çəkinin. Mətn və üslub dəyişməyibsə, measure() nəticəsi eyni olacaq. Asinxron ssenarilər üçün ölçməni Deferred və ya LaunchedEffect ilə birləşdirin.
Tez-tez verilən suallar
TextMeasurer mətni render etmədən əvvəl ölçür, onSizeChanged isə sonra. TextMeasurer mətn ölçüsü valideynin layoutuna təsir etdikdə lazımdır; onSizeChanged kompozisiyadan sonra reaktiv məntiq üçün uyğundur.
Bəli, TextMeasurer.measure() inline üslublar, keçidlər və rənglərlə SpannableString-i əhatə edən AnnotatedString qəbul edir. Bütün spanlar ölçmə zamanı nəzərə alınır.
TextMeasurer Skia-render səviyyəsində işləyir, burada bütün ölçülər pikseldir. LocalDensity.current vasitəsilə çevirin: density.run { width.toDp() }.
TextMeasurer xüsusi şrift yüklənənə qədər FontFamily.Default istifadə edir. Dəqiq ölçmə üçün FontResource vasitəsilə yüklənməni gözləyin və async/await istifadə edin.
LazyColumn-un hər elementində mətnin ölçülməsi zəif cihazlarda FPS-i aşağı sala bilər. Optimallaşdırma: nəticəni remember vasitəsilə keşləyin və mətn ölçüsünü məhdudlaşdırın.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun