يحتوي إنشاء النص على العديد من آليات الرسوم المتحركة المدمجة وقد يكون أمرًا مربكًا معرفة أيهما تختار. في ما يلي قائمة بحالات الاستخدام الشائعة للصور المتحركة. بالنسبة معلومات أكثر تفصيلاً حول المجموعة الكاملة من الخيارات المختلفة المتاحة لواجهة برمجة التطبيقات بالنسبة لك، اقرأ مستندات إنشاء الصور المتحركة بالكامل.
تحريك السمات المشتركة القابلة للإنشاء
توفر ميزة Compose واجهات برمجة تطبيقات ملائمة تتيح لك حل العديد من المشاكل الشائعة حالات استخدام الرسوم المتحركة. يوضح هذا القسم كيفية إنشاء تأثيرات متحركة شائعة. الخاصة بالعنصر القابل للإنشاء.
صورة متحركة تظهر / تختفي
![عنصر أخضر قابل للإنشاء يظهر ويخفي نفسه](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=6&hl=ar)
استخدِم AnimatedVisibility
لإخفاء محتوى قابل للإنشاء أو إظهاره. الأطفال في الداخل
بإمكان "AnimatedVisibility
" استخدام Modifier.animateEnterExit()
لتسجيل الدخول الخاص.
أو الخروج من العنصر الانتقالي.
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
تتيح لك مَعلمتا الدخول والخروج في AnimatedVisibility
ضبط طريقة الإعداد.
عند ظهور واختفاء العنصر القابل للإنشاء. قراءة الكامل
Google لمزيد من المعلومات.
هناك خيار آخر لتحريك مستوى رؤية عنصر قابل للإنشاء، وهو تحريك
مع مرور الوقت باستخدام animateFloatAsState
:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
ومع ذلك، فإن تغيير قيمة ألفا يأتي مع تنبيه بأن العنصر القابل للإنشاء يظل
في المقطوعة الموسيقية واستمرّ شغلها في المساحة الموضوعة هذا النمط
إلى جعل برامج قراءة الشاشة وآليات إمكانية الوصول الأخرى تأخذ في الاعتبار
العنصر المعروض على الشاشة. من ناحية أخرى، يزيل AnimatedVisibility
في النهاية
العنصر من المقطوعة الموسيقية.
![تحريك ألفا لعنصر قابل للإنشاء](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_alpha.gif?authuser=6&hl=ar)
تحريك لون الخلفية
![قابلة للتركيب مع تغيير لون الخلفية بمرور الوقت كرسوم متحركة، حيث تتلاشى الألوان مع بعضها البعض.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=6&hl=ar)
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
هذا الخيار أكثر فعالية من استخدام Modifier.background()
.
يُسمح باستخدام Modifier.background()
لإعدادات الألوان ذات اللقطة الواحدة، ولكن إذا
وتحريك اللون بمرور الوقت، فقد يتسبب ذلك في عمليات إعادة التركيب أكثر من
اللازمة.
لتحريك لون الخلفية بشكل غير محدود، يمكنك الاطّلاع على تكرار صورة متحركة .
تحريك حجم عنصر قابل للإنشاء
![صورة متحركة قابلة للإنشاء ذات اللون الأخضر تغيّر حجمها بسلاسة.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_size.gif?authuser=6&hl=ar)
تتيح لك ميزة "إنشاء" إضافة تأثيرات حركية إلى حجم العناصر القابلة للإنشاء بعدة طرق مختلفة. استخدام
animateContentSize()
للصور المتحركة التي تتغير بين تغييرات الحجم القابلة للإنشاء.
على سبيل المثال، إذا كان لديك مربع يحتوي على نص يمكن توسعته من واحد إلى
التي يمكن أن تتيح لك استخدام "Modifier.animateContentSize()
" للحصول على تجربة أكثر سلاسة
تأثير الانتقال:
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
ويمكنك أيضًا استخدام العلامة AnimatedContent
، مع SizeTransform
لوصف.
كيفية إجراء تغييرات الحجم.
تحريك موضع العنصر القابل للإنشاء
![أخضر قابل للإنشاء مع تحريك سلس لأسفل ولليمين](https://developer.android.com/static/develop/ui/compose/images/animations/animated_offset.gif?authuser=6&hl=ar)
لتحريك موضع عنصر قابل للإنشاء، استخدِم السمة Modifier.offset{ }
مع
animateIntOffsetAsState()
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
إذا كنت تريد التأكد من عدم رسم عناصر قابلة للإنشاء فوق أو أسفل
من العناصر القابلة للإنشاء عند تحريك الموضع أو الحجم، استخدِم Modifier.layout{ }
. هذا النمط
تنشر مفاتيح التعديل تغييرات الحجم والموضع على العنصر الرئيسي، ما يؤثر بعد ذلك
أطفال آخرين.
على سبيل المثال، إذا كنت تنقل Box
ضمن Column
والعناصر الفرعية الأخرى
بحاجة إلى التحرّك عند تحريك Box
، فقم بتضمين معلومات الإزاحة مع
Modifier.layout{ }
على النحو التالي:
var toggled by remember { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } Column( modifier = Modifier .padding(16.dp) .fillMaxSize() .clickable(indication = null, interactionSource = interactionSource) { toggled = !toggled } ) { val offsetTarget = if (toggled) { IntOffset(150, 150) } else { IntOffset.Zero } val offset = animateIntOffsetAsState( targetValue = offsetTarget, label = "offset" ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) Box( modifier = Modifier .layout { measurable, constraints -> val offsetValue = if (isLookingAhead) offsetTarget else offset.value val placeable = measurable.measure(constraints) layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) { placeable.placeRelative(offsetValue) } } .size(100.dp) .background(colorGreen) ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) }
![مربعين يحتوي المربع الثاني على تحريك موضعه X،Y، ويستجيب المربع الثالث بنقل نفسه بمقدار Y أيضًا.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_layout_modifier.gif?authuser=6&hl=ar)
Modifier.layout{ }
"تحريك المساحة المتروكة لعنصر قابل للإنشاء
![عنصر أخضر قابل للإنشاء يتخطّى حجمه عند النقر، مع تحريك المساحة المتروكة](https://developer.android.com/static/develop/ui/compose/images/animations/animated_padding.gif?authuser=6&hl=ar)
لتحريك المساحة المتروكة في عنصر قابل للإنشاء، استخدِم السمة animateDpAsState
مع
Modifier.padding()
:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
تحريك الارتفاع لعنصر قابل للإنشاء
لتحريك ارتفاع عنصر قابل للإنشاء، استخدِم السمة animateDpAsState
مع
Modifier.graphicsLayer{ }
لإجراء تغييرات الارتفاع لمرة واحدة، استخدم
Modifier.shadow()
إذا كنت تقوم بتحريك الظل، باستخدام
مفتاح التعديل Modifier.graphicsLayer{ }
هو الخيار الأفضل أداءً.
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
بدلاً من ذلك، استخدِم Card
القابل للإنشاء واضبط خاصية الارتفاع على
قيمًا مختلفة لكل حالة.
تحريك مقياس النص أو ترجمته أو تدويره
![نص قابل للإنشاء يقول](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text.gif?authuser=6&hl=ar)
عند تحريك مقياس أو ترجمة النص أو تدويره، اضبط السمة textMotion
على TextStyle
إلى TextMotion.Animated
. وهذا يضمن تجربة أكثر سلاسة
الانتقالات بين الرسوم المتحركة النصية. استخدام Modifier.graphicsLayer{ }
من أجل
ترجمة النص أو تدويره أو تغيير حجمه.
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val scale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 8f, animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "scale" ) Box(modifier = Modifier.fillMaxSize()) { Text( text = "Hello", modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale transformOrigin = TransformOrigin.Center } .align(Alignment.Center), // Text composable does not take TextMotion as a parameter. // Provide it via style argument but make sure that we are copying from current theme style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated) ) }
تحريك لون النص
![الكلمات](https://developer.android.com/static/develop/ui/compose/images/animations/animated_text_color.gif?authuser=6&hl=ar)
لتحريك لون النص، استخدِم lambda color
في عنصر BasicText
قابل للإنشاء:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
التبديل بين أنواع مختلفة من المحتوى
![شاشة خضراء تقول](https://developer.android.com/static/develop/ui/compose/images/animations/animated_content_slower.gif?authuser=6&hl=ar)
يمكنك استخدام AnimatedContent
لإنشاء تأثيرات متحركة بين عناصر مختلفة قابلة للإنشاء، في حال:
فقط أريد تلاشيًا عاديًا بين العناصر القابلة للإنشاء، استخدم Crossfade
.
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
يمكن تخصيص AnimatedContent
لعرض العديد من الأنواع المختلفة من الدخول
انتقالات الخروج. لمزيد من المعلومات، اقرأ الوثائق على
AnimatedContent
أو يمكنك قراءة مشاركة المدونة هذه على
AnimatedContent
التحريك أثناء الانتقال إلى وجهات مختلفة
![عنصران قابلان للإنشاء، أحدهما يشير إلى "الهبوط" والآخر باللون الأزرق، "التفاصيل" يرمز إلى التفاصيل، وتتحرك تلك التفاصيل عن طريق تمرير التفاصيل القابلة للإنشاء فوق الصفحة المقصودة القابلة للتركيب.)](https://developer.android.com/static/develop/ui/compose/images/animations/navigation_compose_animation.gif?authuser=6&hl=ar)
لتحريك الانتقالات بين العناصر القابلة للإنشاء عند استخدام
navigation-compos، لتحديد enterTransition
exitTransition
على عنصر قابل للإنشاء. يمكنك أيضًا تعيين الرسوم المتحركة الافتراضية لتكون
مُستخدَمة لجميع الوجهات في المستوى الأعلى NavHost
:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
هناك العديد من أنواع انتقالات الدخول والخروج المختلفة التي تنطبق التأثيرات المختلفة على المحتوى الوارد والصادر، يمكنك الاطلاع على المستندات لمزيد من المعلومات.
تكرار صورة متحركة
![خلفية خضراء تتحول إلى خلفية زرقاء بلا نهاية من خلال إضافة تأثيرات متحركة بين اللونين.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=6&hl=ar)
استخدام rememberInfiniteTransition
مع infiniteRepeatable
animationSpec
لتكرار الرسم المتحرك باستمرار. تغيير RepeatModes
إلى
وتحديد كيفية الانتقال ذهابًا وإيابًا.
استخدِم finiteRepeatable
لتكرار عدد محدد من المرات.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
بدء صورة متحركة عند تشغيل عنصر قابل للإنشاء
يتم تشغيل LaunchedEffect
عند إدخال عنصر قابل للإنشاء في المقطوعة الموسيقية. تبدأ في
رسم متحرك عند تشغيل عنصر قابل للإنشاء، يمكنك استخدام ذلك لتشغيل الرسوم المتحركة
تغيير الحالة. إنّ استخدام Animatable
مع الطريقة animateTo
لبدء
الرسوم المتحركة عند التشغيل:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
إنشاء صور متحركة متسلسلة
![أربع دوائر بها أسهم خضراء تتحرك بين كل دائرة، وتتحرك واحدة تلو الأخرى.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties_sequential.gif?authuser=6&hl=ar)
استخدام واجهات برمجة تطبيقات الكوروتين Animatable
لإجراء تسلسلي أو متزامن
والرسوم المتحركة. الاتصال بالرقم animateTo
على الرقم Animatable
بعد أحد الأسباب الأخرى
كل رسم متحرك لانتظار انتهاء الرسوم المتحركة السابقة قبل المتابعة .
هذا لأنها دالة تعليق.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
إنشاء صور متحركة متزامنة
![ثلاث دوائر بها أسهم خضراء تتحرك على كل دائرة، وتتحرك جميعها معًا في الوقت نفسه.](https://developer.android.com/static/develop/ui/compose/images/animations/multiple_properties.gif?authuser=6&hl=ar)
استخدام واجهات برمجة تطبيقات الكوروتين (Animatable#animateTo()
أو animate
)، أو
واجهة برمجة التطبيقات Transition
لتنفيذ الصور المتحركة المتزامنة. إذا كنت تستخدم عدة
وإطلاق الدوال في سياق الكوروتين، فإنها تُطلق الرسوم المتحركة في نفس
الوقت:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
يمكنك استخدام updateTransition
API لاستخدام الحالة نفسها في Drive.
العديد من الرسوم المتحركة المختلفة للخصائص في نفس الوقت. يتحرك المثال أدناه
هناك خاصيتان يتم التحكّم فيهما من خلال تغيير الحالة، وهما rect
وborderWidth
:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "rect") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "borderWidth") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
تحسين أداء الصور المتحركة
يمكن أن تتسبب الصور المتحركة في Compose في حدوث مشاكل في الأداء. هذا بسبب طبيعة حول الرسم المتحرك: تحريك أو تغيير وحدات البكسل على الشاشة بسرعة، كل إطار على حدة لخلق وهم بالحركة.
مراعاة المراحل المختلفة في Compose: التكوين والتخطيط والرسم. في حال حذف تغير الرسوم المتحركة مرحلة التخطيط، فإنها تتطلب من جميع العناصر القابلة للإنشاء المتأثرة وترحيلهم وإعادة الرسم. إذا كانت الرسوم المتحركة تظهر في مرحلة الرسم، فهي يكون الأداء تلقائيًا أكثر فعالية ممّا لو كنت تشغّل الرسوم المتحركة في حيث سيكون للقيام بعمل أقل بشكل عام.
لضمان أن يؤدي تطبيقك إلى أقل قدر ممكن من الوقت أثناء إنشاء الصور المتحركة، اختَر lambda
من Modifier
متى أمكن ذلك. يؤدي هذا إلى تخطي إعادة التركيب وتنفيذ
الرسوم المتحركة خارج مرحلة الإنشاء، وإلا،
Modifier.graphicsLayer{ }
، لأنّ هذا المعدِّل يعمل دائمًا في عملية الرسم
بنجاح. لمزيد من المعلومات عن هذا الأمر، راجِع قسم قراءات التأجيل في
وثائق الأداء.
تغيير توقيت الرسوم المتحركة
تستخدم ميزة "إنشاء" الصور المتحركة الربيعي تلقائيًا في معظم الصور المتحركة. ينابيع
الرسوم المتحركة القائمة على الفيزياء، تبدو طبيعية أكثر. كما أنها يمكن أن تقاطعها
يأخذ في الاعتبار السرعة الحالية للجسم، بدلاً من الوقت الثابت.
إذا كنت تريد إلغاء الإعداد التلقائي، فستجد أن جميع واجهات برمجة تطبيقات الرسوم المتحركة الموضحة أعلاه
لديه القدرة على ضبط animationSpec
لتخصيص طريقة تشغيل الصور المتحركة،
ما إذا كنت تريد تنفيذه خلال مدة معينة أو أن يكون أكثر مرونة.
في ما يلي ملخّص لخيارات "animationSpec
" المختلفة:
spring
: صورة متحركة مستندة إلى الفيزياء، وهي الخيار التلقائي لكل الصور المتحركة. إِنْتَ تغيير الصلابة أو نسبة التخميد لإنشاء رسم متحرك مختلف الشكل والأسلوب.tween
(اختصار يعبِّر عن بين): صور متحركة مستندة إلى المدة بين قيمتين باستخدام دالةEasing
.keyframes
: مواصفات لتحديد قيم عند نقاط رئيسية معينة في الرسوم المتحركة.repeatable
: مواصفات مستندة إلى المدة ويتم تشغيلها لعدد معين من المرات، المحددة بواسطةRepeatMode
.infiniteRepeatable
: مواصفات مستندة إلى المدة تعمل بشكل دائم.snap
: الانتقال فورًا إلى القيمة النهائية بدون أي مؤثر حركي
![اكتب النص البديل هنا](https://developer.android.com/static/develop/ui/compose/images/animations/animated_spec_set.gif?authuser=6&hl=ar)
اقرأ المستندات الكاملة لمزيد من المعلومات عن animationSpecs.
مصادر إضافية
للاطلاع على المزيد من الأمثلة على الصور المتحركة الممتعة في Compose، يمكنك الاطّلاع على ما يلي:
- 5 صور متحركة سريعة في Compose
- تحريك قنديل البحر في Compose
- تخصيص
AnimatedContent
في Compose - إرخاء دوال التخفيف في Compose