لتخصيص طريقة تشغيل الرسوم المتحركة للانتقال بين العناصر المشترَكة، هناك بعض المَعلمات التي يمكن استخدامها لتغيير طريقة انتقال العناصر المشترَكة.
مواصفات الصورة المتحركة
لتغيير مواصفات الحركة المستخدَمة في تغيير الحجم والموضع، يمكنك تحديد مَعلمة 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.
boundsTransform مختلفةوضع تغيير الحجم
عند تحريك العناصر بين حدود مشتركة، يمكنك ضبط المَعلمة resizeMode على RemeasureToBounds أو ScaleToBounds. تحدّد هذه المَعلمة كيفية انتقال العنصر المشترَك بين الحالتَين. تقيس السمة ScaleToBounds first
تنسيق العنصر التابع باستخدام قيود البحث المسبق (أو القيود المستهدَفة). بعد ذلك، يتم تغيير حجم التصميم الثابت الخاص بالطفل ليتناسب مع الحدود المشتركة.
يمكن اعتبار ScaleToBounds "مقياسًا بيانيًا" بين الحالات.
في المقابل، تعيد الدالة RemeasureToBounds قياس وتنسيق التنسيق الفرعي sharedBounds مع قيود ثابتة متحركة استنادًا إلى الحجم المستهدف. يتم تشغيل إعادة القياس عند تغيير حجم الحدود، وهو ما قد يحدث في كل لقطة.
بالنسبة إلى العناصر القابلة للإنشاء Text، يُنصح باستخدام ScaleToBounds لأنّه يتجنّب إعادة التخطيط وإعادة ترتيب النص على أسطر مختلفة. يُنصح باستخدام RemeasureToBounds
للحدود التي تختلف فيها نسبة العرض إلى الارتفاع، وإذا كنت تريد انتقالًا سلسًا
بين العنصرَين المشترَكَين.
يمكن ملاحظة الفرق بين وضعَي تغيير الحجم في الأمثلة التالية:
|
|
|---|---|
تفعيل العناصر المشترَكة وإيقافها بشكل ديناميكي
يتم تلقائيًا ضبط sharedElement() وsharedBounds() لتحريك تغييرات التصميم كلما تم العثور على مفتاح مطابق في الحالة المستهدَفة. ومع ذلك، قد تحتاج إلى إيقاف هذه الصورة المتحركة بشكل ديناميكي استنادًا إلى شروط معيّنة، مثل اتجاه التنقّل أو حالة واجهة المستخدم الحالية.
للتحكّم في ما إذا كان سيحدث انتقال العنصر المشترَك، يمكنك تخصيص SharedContentConfig الذي تم تمريره إلى rememberSharedContentState(). تحدّد السمة isEnabled ما إذا كان العنصر المشترَك نشطًا.
يوضّح المثال التالي كيفية تحديد إعدادات تتيح الانتقال المشترك فقط عند التنقّل بين شاشات معيّنة (على سبيل المثال، من الشاشة A إلى الشاشة B فقط)، مع إيقافها للشاشات الأخرى.
SharedTransitionLayout { val transition = updateTransition(currentState) transition.AnimatedContent { targetState -> // Create the configuration that depends on state changing. fun animationConfig() : SharedTransitionScope.SharedContentConfig { return object : SharedTransitionScope.SharedContentConfig { override val SharedTransitionScope.SharedContentState.isEnabled: Boolean // For this example, we only enable the transition in one direction // from A -> B and not the other way around. get() = transition.currentState == "A" && transition.targetState == "B" } } when (targetState) { "A" -> Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } "B" -> { Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } } } } }
إذا تم إيقاف عنصر مشترك أثناء حركة مستمرة، سيتم تلقائيًا إكمال الحركة الحالية الجارية لمنع إزالة الحركات أثناء التنفيذ عن طريق الخطأ. إذا كنت بحاجة إلى إزالة العنصر أثناء تقدّم الرسوم المتحركة، يمكنك تجاهل shouldKeepEnabledForOngoingAnimation في واجهة SharedContentConfig لعرض القيمة false.
التخطّي إلى التنسيق النهائي
عند الانتقال بين تخطيطَين، يتم تلقائيًا تحريك حجم التخطيط بين حالته الأولية والنهائية. وقد يكون هذا السلوك غير مرغوب فيه عند تحريك محتوى، مثل النص.
يوضّح المثال التالي نص الوصف "Lorem Ipsum" الذي يظهر على الشاشة بطريقتين مختلفتين. في المثال الأول، يتم إعادة ترتيب النص عند إدخاله مع زيادة حجم الحاوية. في المثال الثاني، لا يتم إعادة ترتيب النص عند توسيعه. تؤدي إضافة Modifier.skipToLookaheadSize() إلى منع إعادة التدفق أثناء زيادة حجمه.
لا |
|
|---|---|
المقاطع والعناصر المركّبة
لكي تتشارك العناصر المشترَكة بين عناصر مختلفة قابلة للإنشاء، يتم رفع مستوى عرض العنصر القابل للإنشاء إلى تراكب طبقة عند بدء الانتقال إلى العنصر المطابق في الوجهة. ويؤدي ذلك إلى تجاوز حدود العنصر الأصل وتحويلات الطبقة (مثل الشفافية والحجم).
سيتم عرضه فوق عناصر واجهة المستخدم الأخرى غير المشترَكة. بعد انتهاء عملية الانتقال، سيتم نقل العنصر من التراكب إلى 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 في أعلى العنصر المشترَك إلى أن تصبح الشاشة غير مرئية. تؤدي إضافة المعدِّل إلى العنصر القابل للإنشاء إلى إبقائه مرتفعًا.
بدون |
باستخدام |
|---|---|
قد تريد أن يتم تحريك الدالة المركّبة غير المشترَكة بعيدًا بالإضافة إلى أن تظل في أعلى الدوال المركّبة الأخرى قبل الانتقال. في مثل هذه الحالات، استخدِم
renderInSharedTransitionScopeOverlay().animateEnterExit() لتحريك
العنصر القابل للإنشاء أثناء تنفيذ عملية الانتقال بين العناصر المشترَكة:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
في الحالات النادرة التي تريد فيها عدم عرض العنصر المشترَك في تراكب، يمكنك ضبط renderInOverlayDuringTransition على sharedElement() على "خطأ".
إشعار التنسيقات المتجاورة بالتغييرات التي تم إجراؤها على حجم العنصر المشترَك
بشكلٍ تلقائي، لا يرسل العنصران sharedBounds() وsharedElement() إشعارًا إلى الحاوية الرئيسية بشأن أي تغييرات في الحجم أثناء انتقالات التنسيق.
لنشر تغييرات الحجم إلى الحاوية الرئيسية أثناء انتقالها،
غيِّر المَعلمة placeholderSize إلى PlaceholderSize.AnimatedSize. سيؤدي ذلك إلى تكبير العنصر أو تصغيره. تستجيب جميع العناصر الأخرى في التنسيق للتغيير.
|
(لاحظ كيف تتحرك العناصر الأخرى في القائمة إلى الأسفل استجابةً للعنصر الذي يزداد حجمه) |
|---|---|