معیارهای خط (Line Metrics) در فلاتر — به زبان ساده

۱۳۷ بازدید
آخرین به‌روزرسانی: ۰۴ مهر ۱۴۰۲
زمان مطالعه: ۴ دقیقه
معیارهای خط (Line Metrics) در فلاتر — به زبان ساده

در گذشته بسیاری از ابزارهای سطح پایین برای کار با متن در فلاتر در لایه نیتیو از دید توسعه‌دهنده پنهان بودند. اما در نسخه‌های اخیر بسیاری از موارد مرتبط با Line Metrics در Dart افشا شده‌اند. در این مقاله به بررسی مفهوم Line Metrics در فلاتر می‌پردازیم.

Line Metrics چیست؟

در فلاتر یک کلاس جدید به نام «معیارهای خط» (Line Metrics) (+) وجود دارد که اطلاعاتی در مورد خطوط منفرد یک پاراگراف که لی‌آوت‌بندی شده است ارائه می‌کند.

این کلاس با حذف سازنده و توضیحات به صورت زیر است:

1class LineMetrics {
2  final bool hardBreak;
3  final double ascent;
4  final double descent;
5  final double unscaledAscent;
6  final double height;
7  final double width;
8  final double left;
9  final double baseline;
10  final int lineNumber;
11}

در کلاس فوق:

  • hard break: تعیین می‌کند آیا خط با یک کاراکتر خط جدید مانند ‎\n پایان یافته یا نه.
  • Ascent: فاصله از خط مبنا تا بالای خط را تعیین می‌کند.
  • Descent: فاصله از خط مبنا تا انتهای خط را تعیین می‌کند.
  • unscaled ascent: همان accent با نادیده گرفتن مضرب TextStyle.height است.
  • height: فاصله از انتها تا بالای خط را تعیین می‌کند.
  • width: فاصله از سمت چپ تا سمت راست خط را تعیین می‌کند.
  • Baseline: خط مبنا یا خطی که متن روی آن قرار می‌گیرد، از بالای پاراگراف و نه بالای خط اندازه‌گیری می‌شود.
  • line number: عدد خط جاری در پاراگراف که از اندیس صفر آغاز می‌شود.

چگونه Line Metrics را به دست آوریم؟

فهرستی از معیارهای خط را می‌توانید با استفاده از کلاس TextPainter به دست آورید:

1List<LineMetrics> lines = textPainter.computeLineMetrics();

همچنین می‌توانید از کلاس Paragraph استفاده کنید. در واقع کلاس TextPainter نیز در پس‌زمینه این را فرا می‌خواند:

1List<LineMetrics> lines = paragraph.computeLineMetrics();

بررسی یک مثال

فرض کنید رشته‌ای مانند زیر داریم:

My text line.\nThis line wraps to the next.\nAnother line.

همچنین تصور کنید محدودیت عرض لی‌آوت را نیز به قدر کافی کوچک تعیین کرده‌ایم تا یک soft-wrap روی طولانی‌ترین خط اعمال شود و بدین ترتیب نتیجه زیر حاصل شود:

Line Metrics در فلاتر

باکس‌های قرمز و خطوط آبی بر اساس مقادیر LineMetrics ترسیم شده‌اند. خطوط آبی، خط مبنا (Baseline) هستند. داده‌های خام هر یک از چهار خط به صورت زیر است:

hardBreak = true
ascent = 27.83203125
descent = 7.32421875
unscaledAscent = 27.83203125
height = 35.0
width = 160.35546875
left = 0.0
baseline = 27.67578125
lineNumber = 0
hardBreak = false
ascent = 27.83203125
descent = 7.32421875
unscaledAscent = 27.83203125
height = 35.0
width = 284.78125
left = 0.0
baseline = 62.67578125
lineNumber = 1
hardBreak = true
ascent = 27.83203125
descent = 7.32421875
unscaledAscent = 27.83203125
height = 35.0
width = 64.78515625
left = 0.0
baseline = 97.67578125
lineNumber = 2
hardBreak = true
ascent = 27.83203125
descent = 7.32421875
unscaledAscent = 27.83203125
height = 35.0
width = 167.4921875
left = 0.0
baseline = 132.67578125
lineNumber = 3

معیارهای خط چه اهمیتی دارند؟

ابتدا باید اشاره کنیم که کلاس LineMetrics همه داده‌های معیار را برای هر خط، در یک جا گردآوری کرده است. محاسبه این موارد در گذشته بسیار دشوار بود. دوم این که داشتن یک فهرست از معیارهای خط موجب شده که یافتن تعداد خطوط بسیار آسان شود:

1List<ui.LineMetrics> lines = textPainter.computeLineMetrics();
2int numberOfLines = lines.length;

در گذشته تنها گزینه ما دریافت فهرستی از TextBoxes بود، اما زمانی که متن‌های دوطرفه وجود داشته باشند، اتفاقات پیچیده‌ای رخ می‌داد. در این حالت امکان محاسبه تعداد باکس‌ها وجود نداشت، زیرا باکس‌ها به‌صورت زیر ظاهر می‌شدند:

Line Metrics در فلاتر اما زمانی که از کلاس LineMetrics استفاده کنیم نتیجه زیر به دست می‌آید:

Line Metrics در فلاتر

محدودیت‌های LineMetrics

استفاده از کلاس LineMetrics قطعاً برای کسانی که می‌خواهند کارهای سطح پایینی در مورد رندر کردن متن انجام دهند، مسیر مناسبی محسوب می‌شود. با این حل باید به نکات زیر توجه داشته باشید:

  • LineMetrics همچنان تصور می‌کند که متن دارای لی‌آوت افقی استاندارد LTR یا RTL در یک باکس مستطیلی است. اگر از لی‌آوت های سفارشی استفاده می‌کنید که هر کلمه به صورت یک پاراگراف جداگانه رندر می‌شود، کلاس LineMetrics نمی‌تواند کمک چندانی به شما کمک کند.
  • در کلاس LineMetrics دریافت متن واقعی هر خط کار دشواری است، گرچه استفاده از آن به چنین منظوری نیز چندان متداول نیست. در این موارد باید از ترکیبی از معیارها و فراخوانی TextPainter.getPositionForOffset استفاده کنید.

البته موارد فوق مشکلات بزرگی محسوب نمی‌شوند و در حالت کلی استفاده از این کلاس بسیار مفید است.

تیم فلاتر از تمایل توسعه‌دهندگان برای داشتن ابزاری مانند یک line breaker و امکان اندازه‌گیری و رسم متن به شیوه‌ای کارآمد آگاه هستند. با این حال در این موارد باید ابتدا بحث و بررسی بیشتری صورت بگیرد.

کد تکمیلی

اگر می‌خواهید با کلاس LineMetrics بیشتر کار کنید، می‌توانید کد زیر را در یک پروژه جدید فلاتر کپی کنید. توجه داشته باشید که باید از نسخه‌ای از فلاتر استفاده کنید که شامل LineMetrics باشد.

1import 'package:flutter/material.dart';
2import 'dart:ui' as ui;
3void main() => runApp(MyApp());
4class MyApp extends StatelessWidget {
5  @override
6  Widget build(BuildContext context) {
7    return MaterialApp(
8      home: Scaffold(
9        body: HomeWidget(),
10      ),
11    );
12  }
13}
14class HomeWidget extends StatelessWidget {
15  @override
16  Widget build(BuildContext context) {
17    return Center(
18      child: CustomPaint(
19        size: Size(300, 300),
20        painter: MyPainter(),
21      ),
22    );
23  }
24}
25class MyPainter extends CustomPainter {
26  final text = 'My text line.\nThis line wraps to the next.\nAnother line.';
27  //final text = 'My text line.\nThis كلمة makes more boxes.\nAnother line.';
28final bluePaint = Paint()
29    ..color = Colors.blue
30    ..style = PaintingStyle.stroke
31    ..strokeWidth = 1;
32final redPaint = Paint()
33    ..color = Colors.red
34    ..style = PaintingStyle.stroke
35    ..strokeWidth = 1;
36@override
37  void paint(Canvas canvas, Size size) {
38    final textStyle = TextStyle(
39      color: Colors.black,
40      fontSize: 30,
41    );
42    final textSpan = TextSpan(
43      text: text,
44      style: textStyle,
45    );
46    final textPainter = TextPainter(
47      text: textSpan,
48      textDirection: TextDirection.ltr,
49    );
50    textPainter.layout(
51      minWidth: 0,
52      maxWidth: size.width,
53    );
54    final offset = Offset(0, 0);
55    textPainter.paint(canvas, offset);
56List<ui.LineMetrics> lines = textPainter.computeLineMetrics();
57for (ui.LineMetrics line in lines) {
58      final baseline = line.baseline;
59      final left = line.left;
60      final top = line.baseline - line.ascent;
61      final right = left + line.width;
62      final bottom = line.baseline + line.descent;
63      final rect = Rect.fromLTRB(left, top, right, bottom);
64canvas.drawLine(
65        Offset(left, baseline),
66        Offset(right, baseline),
67        bluePaint,
68      );
69      canvas.drawRect(rect, redPaint);
70    }
71  }
72@override
73  bool shouldRepaint(CustomPainter old) {
74    return false;
75  }
76}

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
swlh
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *