القوائم التي تتضمن ميزة "الكتابة" على نظام التشغيل Wear OS


تتيح القوائم للمستخدمين اختيار عنصر من مجموعة خيارات على أجهزة Wear OS.

تستخدم العديد من أجهزة Wear OS شاشات مستديرة، ما يجعل رؤية عناصر القائمة التي تظهر بالقرب من أعلى الشاشة وأسفلها أكثر صعوبة. لهذا السبب، يتضمّن Compose for Wear OS إصدارًا من فئة LazyColumnيُسمّى TransformingLazyColumn، والذي يتيح تأثيرات تغيير الحجم والتحويل. عندما تنتقل العناصر إلى الحواف، تصبح أصغر وتتلاشى.

لتطبيق تأثيرات تغيير الحجم والتمرير المقترَحة:

  1. استخدِم Modifier.transformedHeight للسماح لـ Compose باحتساب تغيير الارتفاع أثناء تمرير العنصر على الشاشة.
  2. استخدِم transformation = SurfaceTransformation(transformationSpec) لتطبيق التأثيرات المرئية، بما في ذلك تصغير حجم محتوى العنصر.
  3. استخدِم TransformationSpec مخصّصًا للمكوّنات التي لا تأخذ transformation كمَعلمة، مثل Text.

يعرض الرسم المتحرّك التالي كيفية تغيير حجم عنصر قائمة وتغيير شكله عند الاقتراب من أعلى الشاشة وأسفلها:

يعرض مقتطف الرمز التالي كيفية إنشاء قائمة باستخدام TransformingLazyColumn تنسيق لإنشاء محتوى يبدو رائعًا على مجموعة متنوّعة من أحجام شاشات Wear OS.

يوضّح مقتطف الرمز أيضًا استخدام المعدِّل minimumVerticalContentPadding، الذي يجب ضبطه على عناصر القائمة لتطبيق مساحة العرض الداخلية الصحيحة في أعلى القائمة وأسفلها.

لعرض مؤشر التمرير، شارِك columnState بين ScreenScaffold وTransformingLazyColumn:

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

إضافة تأثير التثبيت والتحريك

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

لإضافة سلوك التثبيت والتحريك، اضبط المَعلمة flingBehavior على TransformingLazyColumnDefaults.snapFlingBehavior(columnState). اضبط rotaryScrollableBehavior ليطابق ذلك، باستخدام RotaryScrollableDefaults.snapBehavior(columnState) للحصول على تجربة متّسقة عند استخدام زر الساعة أو الإطار الفعليَّين.

val columnState = rememberTransformingLazyColumnState()
ScreenScaffold(scrollState = columnState) {
    TransformingLazyColumn(
        state = columnState,
        flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState),
        rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState)
    ) {
        // ...
        // ...
    }
}

التنسيق العكسي

تستند القائمة القابلة للتمرير تلقائيًا إلى حافتها العلوية. إذا مرّر المستخدم إلى أسفل قائمة عادية وتمت إضافة عنصر جديد إلى النهاية، تحتفظ القائمة بعرض المستخدم للعنصر الحالي. على سبيل المثال، إذا كان المستخدم يعرض العنصر 10 في أسفل الشاشة، وتمت إضافة العنصر 11، سيظل العرض مركّزًا على العنصر 10، وسيظهر العنصر 11 خارج الشاشة أسفل العرض الحالي.

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

لدعم حالات الاستخدام هذه، يتيح لك TransformingLazyColumn عكس التنسيق من خلال ضبط reverseLayout = true. يغيّر هذا الإجراء نقطة ارتساء القائمة من الحافة العلوية إلى الحافة السفلية.

لتسهيل الأمر، يؤدي ضبط reverseLayout = true أيضًا إلى عكس الترتيب المرئي للعناصر واتجاه إيماءات التمرير:

  • يتم إنشاء العناصر من الأسفل إلى الأعلى، ما يعني أنّ الفهرس 0 يظهر في أسفل الشاشة.
  • يكشف التمرير للأعلى عن العناصر ذات الفهارس الأعلى.

لإضافة سلوك التثبيت والتحريك بالإضافة إلى التنسيق العكسي، يمكنك دمج flingBehavior وrotaryScrollableBehavior كما هو موضّح في مقتطف الرمز التالي:

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(scrollState = columnState) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding,
        reverseLayout = true,
        modifier = Modifier.fillMaxWidth()
    ) {
        items(10) { index ->
            Button(
                label = {
                    Text(
                        text = "Item ${index + 1}"
                    )
                },
                onClick = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            )
        }
        item {
            // With reverseLayout = true, the last item declared appears at the top.
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text("Header")
            }
        }
    }
}

تعرض الصورتان التاليتان الفرق بين قائمة عادية وقائمة معكوسة:

‫TransformingLazyColumn بتصميم عادي، يعرض العنصر 1 في الأعلى والعناصر بترتيب تصاعدي
الشكل 1. تنسيق قائمة عادية يملأ المحتوى من الأعلى إلى الأسفل.
‫TransformingLazyColumn مع تخطيط معكوس، يعرض العنصر 1 في الأسفل والعناصر بترتيب تنازلي نحو الأعلى
الشكل 2. تنسيق قائمة معكوسة يملأ المحتوى من الأسفل إلى الأعلى.