القياسات الأساسية في تنسيقات Compose

من قواعد Compose أنّه يجب قياس العناصر التابعة مرة واحدة فقط، لأنّ قياسها مرتين يؤدي إلى حدوث استثناء في وقت التشغيل. ومع ذلك، هناك أوقات تحتاج فيها إلى بعض المعلومات عن أطفالك قبل قياسهم.

تتيح لك مقاييس Intrinsics إرسال طلبات بحث عن الأطفال قبل قياس أدائهم فعليًا.

يمكنك طلب IntrinsicSize.Min أو IntrinsicSize.Max من عنصر قابل للإنشاء:

  • Modifier.width(IntrinsicSize.Min) - ما هو الحد الأدنى للعرض المطلوب لعرض المحتوى بشكل صحيح؟
  • Modifier.width(IntrinsicSize.Max) - ما هو الحد الأقصى للعرض الذي تحتاجه لعرض المحتوى بشكل صحيح؟
  • Modifier.height(IntrinsicSize.Min) - ما هو الحد الأدنى للارتفاع المطلوب لعرض المحتوى بشكل صحيح؟
  • Modifier.height(IntrinsicSize.Max) - ما هو الحد الأقصى للارتفاع الذي تحتاجه لعرض المحتوى بشكل صحيح؟

على سبيل المثال، إذا طلبت minIntrinsicHeight Text مع قيود width لا نهائية في تصميم مخصّص، سيتم عرض height Text مع رسم النص في سطر واحد.

استخدام الدالات الجوهرية

لنفترض أنّنا نريد إنشاء عنصر قابل للإنشاء يعرض نصَين على الشاشة مفصولَين بمقسّم على النحو التالي:

عنصران نصيان جنبًا إلى جنب، مع فاصل عمودي بينهما

كيف يمكننا تحقيق ذلك؟ يمكننا الحصول على Row مع Textين في الداخل يتوسّعان قدر الإمكان وDivider في المنتصف. نريد أن يكون Divider طويلاً مثل أطول Text ونحيفًا (width = 1.dp).

@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
        )
        VerticalDivider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

إذا عاينّا هذا الرمز، سنلاحظ أنّ Divider يتوسّع ليملأ الشاشة بأكملها، وهذا ليس ما نريده:

عنصران نصيان جنبًا إلى جنب، مع فاصل بينهما، ولكن يمتد الفاصل إلى أسفل النص

يحدث ذلك لأنّ Row يقيس كل عنصر فرعي على حدة، ولا يمكن استخدام ارتفاع Text لتقييد Divider. نريد أن يملأ Divider المساحة المتاحة بارتفاع محدّد. يمكننا استخدام المعدِّل height(IntrinsicSize.Min) لهذا الغرض .

تفرض height(IntrinsicSize.Min) على العناصر الفرعية أن يكون ارتفاعها مساويًا للحد الأدنى للارتفاع الداخلي. بما أنّها متكرّرة، ستطلب Row وأطفالها minIntrinsicHeight.

عند تطبيق ذلك على الرمز البرمجي، سيعمل على النحو المتوقّع:

@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
        )
        VerticalDivider(
            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")
        }
    }
}

مع معاينة:

عنصران نصيان جنبًا إلى جنب، مع فاصل عمودي بينهما

ستكون قيمة minIntrinsicHeight للعنصر القابل للإنشاء Row هي الحد الأقصى minIntrinsicHeight للعناصر التابعة له. قيمة Divider للعنصر minIntrinsicHeight هي 0 لأنّه لا يشغل أي مساحة إذا لم يتم تحديد أي قيود، أما قيمة Text minIntrinsicHeight فستكون قيمة النص مع تحديد width معيّن. وبالتالي، سيكون القيد height الخاص بالعنصر Row هو الحد الأقصى minIntrinsicHeight من Text. بعد ذلك، سيوسّع Divider نطاق height إلى الحدّ height الذي يحدّده Row.

السمات الجوهرية في التنسيقات المخصّصة

عند إنشاء معدِّل Layout أو layout مخصّص، يتم احتساب القياسات الجوهرية تلقائيًا استنادًا إلى تقديرات. لذلك، قد لا تكون الحسابات صحيحة لجميع التنسيقات. وتوفّر واجهات برمجة التطبيقات هذه خيارات لتجاوز هذه الإعدادات التلقائية.

لتحديد قياسات السمات الجوهرية الخاصة بعنصر Layout المخصّص، عليك تجاهل minIntrinsicWidth وminIntrinsicHeight وmaxIntrinsicWidth وmaxIntrinsicHeight في واجهة MeasurePolicy عند إنشائها.

@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.
        }
    )
}

عند إنشاء معدِّل layout مخصّص، عليك إلغاء الطرق ذات الصلة في واجهة LayoutModifier.

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.
}