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

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

تسمح لك السمات الأساسية باستعلام الأطفال قبل قياسهم فعليًا.

يمكنك طلب intrinsicWidth أو intrinsicHeight للعنصر القابل للتجميع:

  • (min|max)IntrinsicWidth: استنادًا إلى هذا العرض، ما هو الحد الأدنى/الحد الأقصى للعرض الذي يمكنك من خلاله رسم المحتوى بشكل صحيح؟
  • (min|max)IntrinsicHeight: استنادًا إلى هذا الارتفاع، ما هو الحد الأدنى/الحد الأقصى للارتفاع الذي يمكنك من خلاله رسم المحتوى بشكل صحيح؟

على سبيل المثال، إذا طلبت minIntrinsicHeight من Text مع height لانهائي، سيعرض ال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
        )
        HorizontalDivider(
            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
        )
        HorizontalDivider(
            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 لعناصره الثانوية. قيمة minIntrinsicHeight الخاص بالعنصر Divider هي 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.
}