TextMeasurer Jetpack Compose کا ایک API ہے جو اسکرین پر اصل رینڈرنگ سے پہلے ٹیکسٹ کے طول و عرض کی پیمائش کرنے کے لیے ڈیزائن کیا گیا ہے۔ Android View میں Paint.measureText کے ذریعے کلاسک طریقہ کے برعکس، TextMeasurer اسٹائل اور رکاوٹوں کو مدنظر رکھتے ہوئے چوڑائی، اونچائی اور لائن میٹرکس حاصل کرنے کا Compose-مقامی طریقہ فراہم کرتا ہے۔ Android Developers Documentation (2025) کے مطابق، TextMeasurer حسب ضرورت Layout اجزاء، متحرک لیبلز اور انکولی UI عناصر میں فعال طور پر استعمال ہوتا ہے جہاں ٹیکسٹ کا سائز دوسرے عناصر کی پوزیشننگ کو متاثر کرتا ہے۔
اہم نکات
TextMeasurer androidx.compose.ui.text پیکیج کا ایک فنکشنل انٹرفیس ہے جو اسکرین پر اصل رینڈرنگ کے بغیر Compose میں ٹیکسٹ کی پیمائش کرنے کی اجازت دیتا ہے۔ یہ Compose ٹیکسٹ انجن کا حصہ ہے اور Paragraph API کی سطح پر کام کرتا ہے، جو رینڈرنگ کے لیے Skia استعمال کرتا ہے۔
TextMeasurer کا بنیادی مقصد پیشگی پیمائش ہے۔ Compose میں، ٹیکسٹ کا سائز عام طور پر Composable کے Layout میں رکھے جانے کے بعد متعین ہوتا ہے۔ لیکن ایسے منظرنامے ہیں جہاں آپ کو لے آؤٹ سے پہلے ٹیکسٹ کی چوڑائی جاننے کی ضرورت ہوتی ہے — مثال کے طور پر، یہ فیصلہ کرنے کے لیے کہ ایک سٹرنگ کنٹینر میں فٹ بیٹھتی ہے یا نہیں، یا ٹیکسٹ کی لمبائی کی بنیاد پر بٹن کے سائز کا حساب لگانے کے لیے۔
TextMeasurer measure() طریقہ فراہم کرکے اس مسئلے کو حل کرتا ہے، جو TextMeasureRequest (ٹیکسٹ، اسٹائل، رکاوٹیں) لیتا ہے اور TextLayoutResult (میٹرکس، لائنیں، کریکٹرز) لوٹاتا ہے۔ یہ ڈویلپر کو ایک فریم کی تاخیر کے بغیر، ہم وقت طور پر تمام ٹیکسٹ ڈیٹا حاصل کرنے کی اجازت دیتا ہے۔
TextMeasurer استعمال کریں جب ٹیکسٹ کا سائز پیرنٹ کمپوننٹ کے سائز کو متاثر کرتا ہو، متحرک ٹیکسٹ ٹرانزیشن بناتے وقت، یا حسب ضرورت Layout لاگو کرتے وقت جہاں ٹیکسٹ دوسرے عناصر کے ساتھ موجود ہو۔
TextMeasurer API دو اہم اکائیوں پر مشتمل ہے: rememberTextMeasurer() (مثال بنانا) اور measure() (پیمائش چلانا)۔ مثال کمپوننٹ کی پوری زندگی کے لیے ایک بار بنائی جاتی ہے اور تمام پیمائشوں کے لیے دوبارہ استعمال ہوتی ہے۔
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 پیرامیٹر زیادہ سے زیادہ چوڑائی اور اونچائی متعین کرتا ہے جس میں ٹیکسٹ فٹ ہونا چاہیے۔ اگر ٹیکسٹ چوڑائی میں فٹ نہیں ہوتا، تو یہ اگلی لائن میں چلا جاتا ہے۔ پیمائش کے نتیجے میں TextLayoutResult ہوتا ہے، جو لائنوں کی تعداد، ہر کریکٹر کی پوزیشن، ہر لائن کی چوڑائی اور ٹیکسٹ کا مجموعی سائز فراہم کرتا ہے۔
TextMeasurer UI تھریڈ پر ہم وقت طور پر کام کرتا ہے اور دوبارہ ترکیب کو متحرک نہیں کرتا۔ تاہم، مختلف اسٹائلز کے ساتھ بار بار ٹیکسٹ کی پیمائش اوور ہیڈ پیدا کر سکتی ہے — لہذا مثال remember کے ذریعے بنائی اور دوبارہ استعمال کی جاتی ہے۔
سب سے آسان منظرنامہ پیرنٹ کنٹینر کا سائز متعین کرنے کے لیے ایک لائن والے ٹیکسٹ کی چوڑائی ناپنا ہے۔ مثال کے طور پر، ایک بٹن جس کی چوڑائی لیبل کی لمبائی پر منحصر ہے۔
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)
}
اس مثال میں، remember(label) اس بات کو یقینی بناتا ہے کہ پیمائش صرف اس وقت دوبارہ شروع ہو جب ٹیکسٹ تبدیل ہوتا ہے۔ لائن ریپنگ کو روکنے کے لیے رکاوٹیں بڑی maxWidth (1000) کے ساتھ سیٹ کی گئی ہیں — اصل رکاوٹ ٹیکسٹ کی چوڑائی کے برابر ہوگی۔ Box کی چوڑائی ناپی گئی ٹیکسٹ چوڑائی کے برابر سیٹ کی گئی ہے، جو “ٹیکسٹ کے سائز کا بٹن” اثر دیتی ہے۔
نوٹ: نتیجے سے چوڑائی کو .dp کے ذریعے dp میں تبدیل کرنے کی ضرورت ہے، کیونکہ TextMeasurer طول و عرض پکسلز میں لوٹاتا ہے جبکہ Modifier.width Dp کی توقع کرتا ہے۔ درستگی کے لیے، density اور LocalDensity.current استعمال کریں۔
متعدد لائنوں والے ٹیکسٹ کے لیے، TextMeasurer نہ صرف کل اونچائی بلکہ لائنوں کی تعداد اور پیراگراف کے اندر ہر کریکٹر کی پوزیشن بھی متعین کرنے کی اجازت دیتا ہے۔ یہ حسب ضرورت ٹیکسٹ فیلڈز، چیٹس یا ایڈیٹرز بناتے وقت ضروری ہے۔
متعدد لائنوں والے ٹیکسٹ کے لیے اہم TextLayoutResult خصوصیات: lineCount (لائنوں کی تعداد)، getLineTop(index) (لائن شروع ہونے کا Y-متناسق)، getLineBottom(index) (لائن ختم ہونے کا Y-متناسق)، getLineWidth(index) (لائن کی چوڑائی)۔ ان کو ملا کر، آپ انٹرفیس عناصر کو ٹیکسٹ لائنوں کے نسبت درست طریقے سے پوزیشن کر سکتے ہیں۔
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) طریقہ مخصوص لائن کی چوڑائی پکسلز میں لوٹاتا ہے۔ یہ maxWidth سے کم ہو سکتا ہے اگر لائن کنٹینر کی حد تک نہ پہنچے۔ ہر لائن کی چوڑائی جان کر، آپ دستی طور پر جسٹیفائی اثر لاگو کر سکتے ہیں یا چھوٹی لائنوں کے ساتھ ان لائن عناصر رکھ سکتے ہیں۔
کسی مخصوص کریکٹر کی پوزیشن حاصل کرنے کے لیے، getBoundingBox(offset) استعمال کریں، جو left، top، right، bottom متناسقات کے ساتھ Rect لوٹاتا ہے۔ یہ حسب ضرورت ٹیکسٹ فیلڈز میں کرسر پوزیشننگ یا اینیمیشن کے ساتھ ٹیکسٹ رینج کو ہائی لائٹ کرنے کے لیے مفید ہے۔
TextMeasurer کا سب سے طاقتور استعمال حسب ضرورت Layout اجزاء ہیں، جہاں ٹیکسٹ دوسرے عناصر کے ساتھ موجود ہوتا ہے اور ان کے سائز ایک دوسرے پر منحصر ہوتے ہیں۔ مثال کے طور پر، ایک “آئیکن + ٹیکسٹ” ویجیٹ جہاں آئیکن پورے بلاک کی بجائے ٹیکسٹ کی پہلی لائن کے نسبت مرکز میں ہونا چاہیے۔
ایک معیاری Row میں، آئیکن پوری Row کی اونچائی سے مرکز میں ہوتا ہے، جو غیر فطری لگتا ہے جب ٹیکسٹ 3 لائنیں لیتا ہے جبکہ آئیکن ایک۔ TextMeasurer کے ساتھ، آپ پہلی لائن ناپ سکتے ہیں اور آئیکن کو اس کے ساتھ درست طریقے سے سیدھ میں لا سکتے ہیں۔
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)
}
}
)
اس مثال میں، TextMeasurer کے ذریعے پہلی لائن کی پیمائش آئیکن کی پوزیشن متعین کرتی ہے۔ TextMeasurer کے بغیر، آئیکن متعدد لائنوں والے ٹیکسٹ کی پوری اونچائی سے مرکز میں ہوگا، جو بصری طور پر غلط ہے۔ یہ پیٹرن چیٹس، رابطہ فہرستوں اور معلوماتی کارڈز میں وسیع پیمانے پر استعمال ہوتا ہے۔
TextMeasurer ایک ہم وقت API ہے جو UI تھریڈ پر چلتا ہے۔ ضرورت سے زیادہ استعمال فریم ڈراپ کا سبب بن سکتا ہے، خاص طور پر کمزور آلات پر بڑی ٹیکسٹ مقدار کی پیمائش کرتے وقت۔
پہلا قاعدہ — نتیجہ کیش کریں۔ ہمیشہ measure() کال کو ٹیکسٹ اور اسٹائل انحصار کے ساتھ remember میں لپیٹیں۔ CompositionLocal کے اندر یا گرم دوبارہ ترکیب کے راستوں میں کبھی measure() کال نہ کریں۔
دوسرا قاعدہ — maxWidth اور maxHeight کو محدود کریں۔ اگر آپ maxWidth = Constraints.Infinity سیٹ کرتے ہیں، TextMeasurer لائنیں ریپ نہیں کر سکے گا اور ٹیکسٹ اسکرین سے باہر چلا جائے گا۔ ہمیشہ حقیقت پسندانہ رکاوٹیں دیں، خاص طور پر متعدد لائنوں والے ٹیکسٹ کے لیے۔
تیسرا قاعدہ — ایک ہی پیرامیٹرز کے ساتھ بار بار پیمائش سے گریز کریں۔ اگر ٹیکسٹ اور اسٹائل تبدیل نہیں ہوئے، measure() کا نتیجہ ایک جیسا ہوگا۔ غیر ہم وقت منظرناموں کے لیے پیمائش کو Deferred یا LaunchedEffect کے ساتھ ملائیں۔
اکثر پوچھے گئے سوالات
TextMeasurer رینڈرنگ سے پہلے ٹیکسٹ ناپتا ہے، جبکہ onSizeChanged بعد میں ناپتا ہے۔ TextMeasurer اس وقت ضروری ہے جب ٹیکسٹ کا سائز پیرنٹ لے آؤٹ کو متاثر کرتا ہے؛ onSizeChanged ترکیب کے بعد رد عمل کی منطق کے لیے موزوں ہے۔
ہاں، TextMeasurer.measure() AnnotatedString قبول کرتا ہے، جس میں ان لائن اسٹائلز، لنکس اور رنگوں کے ساتھ SpannableString شامل ہے۔ تمام اسپینز پیمائش کے دوران مدنظر رکھے جاتے ہیں۔
TextMeasurer Skia رینڈرنگ کی سطح پر کام کرتا ہے، جہاں تمام طول و عرض پکسلز میں ہیں۔ LocalDensity.current کے ذریعے تبدیل کریں: density.run { width.toDp() }.
TextMeasurer حسب ضرورت فونٹ لوڈ ہونے تک FontFamily.Default استعمال کرتا ہے۔ درست پیمائش کے لیے، FontResource کے ذریعے فونٹ لوڈ ہونے کا انتظار کریں اور async/await استعمال کریں۔
ہر LazyColumn آئٹم میں ٹیکسٹ ناپنا کمزور آلات پر FPS کم کر سکتا ہے۔ اصلاح: remember کے ذریعے نتیجہ کیش کریں اور ٹیکسٹ کا سائز محدود کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں