تخصيص نقل العنصر المشترَك

لتخصيص طريقة تشغيل الرسوم المتحركة للانتقال بين العناصر المشترَكة، هناك بعض المَعلمات التي يمكن استخدامها لتغيير طريقة انتقال العناصر المشترَكة.

مواصفات الصورة المتحركة

لتغيير مواصفات الحركة المستخدَمة في تغيير الحجم والموضع، يمكنك تحديد مَعلمة boundsTransform مختلفة في Modifier.sharedElement(). يوفّر ذلك موضع Rect الأوّلي وموضع Rect المستهدَف.

على سبيل المثال، لجعل النص في المثال السابق يتحرّك بشكل قوسي، حدِّد المَعلمة boundsTransform لاستخدام مواصفات keyframes:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

يمكنك استخدام أي AnimationSpec. يستخدم هذا المثال مواصفات keyframes.

الشكل 1. مثال يعرض مَعلمات boundsTransform مختلفة

وضع تغيير الحجم

عند تحريك العناصر بين حدود مشترَكة، يمكنك ضبط المَعلمة resizeMode على RemeasureToBounds أو ScaleToBounds. تحدّد هذه المَعلمة كيفية انتقال العنصر المشترَك بين الحالتَين. تقيس السمة ScaleToBounds first تنسيق العنصر التابع باستخدام قيود البحث المسبق (أو القيود المستهدَفة). بعد ذلك، يتم تغيير حجم التصميم الثابت الخاص بالطفل ليتناسب مع الحدود المشتركة. يمكن اعتبار ScaleToBounds "مقياسًا بيانيًا" بين الحالات.

في المقابل، تعيد RemeasureToBounds قياس وتنسيق التنسيق الفرعي الخاص بـ sharedBounds باستخدام قيود ثابتة متحركة استنادًا إلى الحجم المستهدف. يتم تشغيل إعادة القياس عند تغيير حجم الحدود، وقد يحدث ذلك مع كل لقطة.

بالنسبة إلى العناصر القابلة للإنشاء Text، يُنصح باستخدام ScaleToBounds، لأنّه يتجنّب إعادة التخطيط وإعادة ترتيب النص على أسطر مختلفة. يُنصح باستخدام RemeasureToBounds للحدود التي تختلف فيها نسبة العرض إلى الارتفاع، وإذا كنت تريد انتقالًا سلسًا بين العنصرَين المشترَكَين.

يمكن ملاحظة الفرق بين وضعَي تغيير الحجم في الأمثلة التالية:

ScaleToBounds

RemeasureToBounds

الانتقال إلى التنسيق النهائي

عند الانتقال بين تصميمَين، يتم تلقائيًا تحريك حجم التصميم بين حالتَي البداية والنهاية. قد يكون هذا السلوك غير مرغوب فيه عند تحريك محتوى، مثل النص.

يوضّح المثال التالي نص الوصف "Lorem Ipsum" الذي يظهر على الشاشة بطريقتين مختلفتين. في المثال الأول، يتم إعادة تدفق النص عند إدخاله مع زيادة حجم الحاوية. في المثال الثاني، لا يتم إعادة ترتيب النص عند توسيعه. تؤدي إضافة Modifier.skipToLookaheadSize() إلى منع إعادة التدفق أثناء زيادة حجمه.

لا Modifier.skipToLookahead() - لاحظ إعادة تدفّق نص "Lorem Ipsum"

Modifier.skipToLookahead() - لاحظ أنّ نص "Lorem Ipsum" يحتفظ بحالته النهائية في بداية الصورة المتحركة

المقاطع والعناصر المركّبة

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

سيتم عرضه فوق عناصر واجهة المستخدم الأخرى غير المشترَكة. بعد انتهاء عملية النقل، سيتم نقل العنصر من التراكب إلى DrawScope الخاص به.

لقص عنصر مشترك إلى شكل، استخدِم الدالة العادية Modifier.clip(). ضَعها بعد sharedElement():

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

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

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

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

بدون Modifier.renderInSharedTransitionScopeOverlay()

باستخدام Modifier.renderInSharedTransitionScopeOverlay()

قد تريد أن يتم تحريك العنصر القابل للإنشاء غير المشترَك إلى الخارج بالإضافة إلى أن يظل في أعلى العناصر القابلة للإنشاء الأخرى قبل الانتقال. في مثل هذه الحالات، استخدِم renderInSharedTransitionScopeOverlay().animateEnterExit() لتحريك العنصر القابل للإنشاء أثناء تنفيذ عملية الانتقال بين العناصر المشترَكة:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

الشكل 2. شريط التطبيق السفلي ينزلق للداخل والخارج أثناء انتقال الحركة.

في الحالات النادرة التي تريد فيها عدم عرض العنصر المشترَك في تراكب، يمكنك ضبط renderInOverlayDuringTransition على sharedElement() على "خطأ".

إشعار التنسيقات المتجاورة بالتغييرات التي تم إجراؤها على حجم العنصر المشترَك

بشكلٍ تلقائي، لا يرسل sharedBounds() وsharedElement() إشعارًا إلى الحاوية الرئيسية بشأن أي تغييرات في الحجم أثناء انتقال التنسيق.

من أجل نقل تغييرات الحجم إلى الحاوية الرئيسية أثناء انتقالها، غيِّر المَعلمة placeHolderSize إلى PlaceHolderSize.animatedSize. سيؤدي ذلك إلى تكبير العنصر أو تصغيره. تستجيب جميع العناصر الأخرى في التنسيق للتغيير.

PlaceholderSize.contentSize (تلقائي)

PlaceholderSize.animatedSize

(لاحظ كيف تتحرك العناصر الأخرى في القائمة إلى الأسفل استجابةً للعنصر الذي يزداد حجمه)