לכתיבה יש הרבה מנגנוני אנימציה מובנים, אנחנו נדע באיזה מהם לבחור. בהמשך ריכזנו רשימה של תרחישים נפוצים לדוגמה באנימציה. עבור מידע מפורט יותר על הערכה המלאה של האפשרויות הזמינות לממשקי API כדאי לקרוא את התיעוד המלא של אנימציה של כתיבה.
יוצרים אנימציה של תכונות קומפוזביליות נפוצות
'פיתוח נייטיב' מספק ממשקי API נוחים שמאפשרים לכם לפתור בעיות של אנימציה. הקטע הזה מדגים איך אפשר ליצור אנימציות נפוצות של תוכן קומפוזבילי.
הצגת אנימציה / נעלמת
![תוכן קומפוזבילי ירוק שמציג ומסתיר את עצמו](https://developer.android.com/static/develop/ui/compose/images/animations/animated_visibility_column.gif?authuser=0000&hl=he)
אפשר להשתמש ב-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
מאפשרים להגדיר איך
תוכן קומפוזבילי פועל כשהוא מופיע ונעלם. לקריאת הרשימה המלאה
תיעוד למידע נוסף.
אפשרות נוספת לאנימציה של הניראות של תוכן קומפוזבילי היא להוסיף אנימציה
alpha לאורך זמן באמצעות 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=0000&hl=he)
אנימציית צבע רקע
![ניתן בקומפוזביליות עם צבעי רקע שמשתנים עם הזמן כאנימציה, שבה הצבעים מתעמעמים זה לזה.](https://developer.android.com/static/develop/ui/compose/images/animations/animated_forever.gif?authuser=0000&hl=he)
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=0000&hl=he)
התכונה 'פיתוח נייטיב' מאפשרת לכם ליצור אנימציות לגודל של התכנים הקומפוזביליים במספר דרכים שונות. כדאי להשתמש
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=0000&hl=he)
כדי להוסיף אנימציה למיקום של תוכן קומפוזבילי, צריך להשתמש בפונקציה 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=0000&hl=he)
Modifier.layout{ }
אנימציה של המרווח הפנימי של תוכן קומפוזבילי
![תוכן קומפוזבילי ירוק שמצטמצם ונגדל עם לחיצה, עם אנימציה של המרווח הפנימי](https://developer.android.com/static/develop/ui/compose/images/animations/animated_padding.gif?authuser=0000&hl=he)
כדי להוסיף אנימציה למרווח הפנימי של תוכן קומפוזבילי, צריך להשתמש בשילוב של 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=0000&hl=he)
כשיוצרים אנימציה של קנה מידה, תרגום או סיבוב של טקסט, צריך להגדיר את הערך 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=0000&hl=he)
כדי להוסיף אנימציה לצבע הטקסט, צריך להשתמש במבדה 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=0000&hl=he)
משתמשים ב-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
כדי להציג סוגים רבים ושונים של Enter
מעברי יציאה. למידע נוסף, אפשר לקרוא את משאבי העזר בנושא
AnimatedContent
או לקרוא את הפוסט הזה בבלוג ב-
AnimatedContent
יוצרים אנימציה תוך כדי ניווט ליעדים שונים
![שתי תכנים קומפוזביליים, אחת ירוקה עם הכיתוב 'נחיתה' ואחת כחולה עם הכיתוב 'פרטים', ואנימציה שמחליקת את הפרטים הקומפוזביליים של התוכן הקומפוזבילי על גבי הנחיתה הקומפוזבילי.](https://developer.android.com/static/develop/ui/compose/images/animations/navigation_compose_animation.gif?authuser=0000&hl=he)
כדי להוסיף אנימציה למעברים בין תכנים קומפוזביליים כשמשתמשים ברכיב
ארטיפקט Navigation-composition, מציינים את 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=0000&hl=he)
שימוש ב-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=0000&hl=he)
שימוש בממשקי ה-API של coroutine 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=0000&hl=he)
להשתמש בממשקי API של Coroutine (Animatable#animateTo()
או animate
), או
את ה-API של Transition
כדי ליצור אנימציות בו-זמנית. אם אתם משתמשים בכמה
מפעילה פונקציות בהקשר של קורוטין, היא מפעילה את האנימציות
שעה:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
אתם יכולים להשתמש ב-API של updateTransition
כדי להשתמש באותו מצב ל-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 } }
אופטימיזציה של ביצועי האנימציה
אנימציות שמופיעות ב'כתיבה' עלולות לגרום לבעיות בביצועים. הסיבה לכך היא האופי של של מהי אנימציה: הזזה או שינוי של פיקסלים במסך במהירות, פריים אחרי פריים כדי ליצור אשליה של תנועה.
נבחן את השלבים השונים של כתיבת הודעה: הרכבה, פריסה וציור. אם המיקום האנימציה משנה את שלב הפריסה, היא דורשת שכל התכנים הקומפוזביליים המושפעים ולכתוב מחדש. אם האנימציה מתרחשת בשלב השרטוט, היא להניב ביצועים טובים יותר כברירת מחדל מאשר להריץ את האנימציה בפריסה שלב זה, כי הדבר היה מצריך פחות עבודה באופן כללי.
כדי להבטיח שהאפליקציה שלך תבצע פעולות מעטות ככל האפשר בזמן האנימציה, כדאי לבחור ב-lambda
של Modifier
כשהדבר אפשרי. פעולה זו מדלגת על הרכבה מחדש ומבצעת
האנימציה שמחוץ לשלב היצירה. אחרת, השתמשו
Modifier.graphicsLayer{ }
, כי הצירוף הזה תמיד פועל ב-Dock
שלב אחד. למידע נוסף בנושא הזה, אפשר לעיין בקטע דחיית קריאות ב
במסמכי התיעוד בנושא ביצועים.
שינוי תזמון האנימציה
כברירת מחדל, ברוב האנימציות משתמשים באנימציות אביב. אביבים, או
אנימציות שמבוססות על פיזיקה, מרגישות יותר טבעיות. אפשר להפריע להם גם בתור
הם לוקחים בחשבון את המהירות הנוכחית של העצם, ולא את הזמן הקבוע.
אם רוצים לשנות את ברירת המחדל, כל ממשקי ה-API של האנימציה שמופיעים למעלה.
יכולים להגדיר 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=0000&hl=he)
קראו את התיעוד המלא לקבלת מידע נוסף על animationSpecs.
מקורות מידע נוספים
דוגמאות נוספות לאנימציות משעשעות חדשניות ב'כתיבה':
- 5 אנימציות מהירות ב'כתיבה'
- מניעים מדוזות בעזרת 'פיתוח נייטיב'
- התאמה אישית של
AnimatedContent
במצב 'כתיבה' - התאמה לפונקציות של ההתאמה למצב 'כתיבה'