القوائم التي تتضمن ميزة "الكتابة" على نظام التشغيل 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. تنسيق قائمة معكوسة يملأ المحتوى من الأسفل إلى الأعلى.