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

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

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

لتغيير مواصفات الصور المتحركة المستخدمة في حركة الحجم والموضع، يمكنك تحديد معلَمة 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 أولاً التنسيق الفرعي مع قيود واجهة النظر (أو الاستهداف). ثم يتم تغيير حجم التخطيط الثابت للعنصر ليلائم الحدود المشتركة. يمكن اعتبار ScaleToBounds "مقياسًا بيانيًا" بين الحالات.

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

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

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

ScaleToBounds

RemeasureToBounds

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

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

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

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

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

المقاطع والعناصر التي تظهر على سطح الفيديو

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

وسيتم عرضه فوق عناصر واجهة المستخدم الأخرى غير المشتركة. بعد انتهاء عملية الانتقال، سيتم تجاهل العنصر من التراكب إلى 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

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