أساسيات animate*AsState
حرّك القيم المفردة بسلاسة
أساسيات animate*AsState درس مجاني في Android Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Android Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Android Academy 4 دروس في المجموع.
تحريك قيمة واحدة
إن أبسط طريقة لإضافة رسوم متحركة في Compose هي عائلة animate*AsState. تعطونها قيمة مستهدفة، فتعيد لكم State تحرّك القيمة بسلاسة من قيمتها الحالية إلى القيمة المستهدفة كلما تغيّرت القيمة المستهدفة.
يتوفر متغير لكل نوع شائع: animateFloatAsState وanimateDpAsState وanimateColorAsState وanimateOffsetAsState وغير ذلك.
animateFloatAsState
تُحرّك animateFloatAsState قيمة واحدة من النوع Float. ومن الاستخدامات الشائعة لذلك الشفافية (alpha). عندما تتغير قيمة visible، تنساب قيمة alpha المعادة بين 0f و1f.
لاحظوا الكلمة المفتاحية by: فهي تتيح لكم قراءة State<Float> المتحركة مباشرةً على شكل Float عادي.
@Composable
fun FadingLabel(visible: Boolean) {
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
label = "alpha"
)
Text(
"Hello",
modifier = Modifier.graphicsLayer { this.alpha = alpha }
)
}animateDpAsState
تستخدم أحجام واجهة المستخدم وإزاحاتها النوع Dp، لذا استخدموا animateDpAsState لتحريك الحشو أو الحجم أو الموضع.
ينمو مربع هنا من 80dp إلى 160dp عند تبديل الحالة. وبما أننا نمرّر قيمة Dp المتحركة إلى size()، يتغير حجم المربع بسلاسة.
@Composable
fun GrowingBox(big: Boolean) {
val size by animateDpAsState(
targetValue = if (big) 160.dp else 80.dp,
label = "size"
)
Box(
Modifier
.size(size)
.background(Color.Blue)
)
}animateColorAsState
لتحريك الألوان، استخدموا animateColorAsState. إذ يُجري استيفاءً عبر نطاق الألوان، فتبدو الانتقالات سلسة وطبيعية بدلًا من الوميض عبر درجات وسيطة غريبة.
تتحرك نقطة الحالة هنا بين الأحمر والأخضر استنادًا إلى قيمة منطقية.
@Composable
fun StatusDot(online: Boolean) {
val color by animateColorAsState(
targetValue = if (online) Color(0xFF4CAF50) else Color(0xFFF44336),
label = "status-color"
)
Box(
Modifier
.size(16.dp)
.clip(CircleShape)
.background(color)
)
}التخصيص باستخدام animationSpec
تستخدم هذه الأدوات، افتراضيًا، نابضًا لطيفًا. مرّروا animationSpec للتحكم في التوقيت. استخدموا tween لمدة ثابتة مع منحنى تسهيل.
تنزلق هذه اللوحة خلال 400ms بمنحنى سلس يتسارع ثم يتباطأ.
@Composable
fun SlidingPanel(open: Boolean) {
val offset by animateDpAsState(
targetValue = if (open) 0.dp else 250.dp,
animationSpec = tween(
durationMillis = 400,
easing = FastOutSlowInEasing
),
label = "offset"
)
Box(Modifier.offset(x = offset)) { /* content */ }
}منحنيات التسهيل
يحدد التسهيل كيفية تسارع الرسوم المتحركة وتباطؤها. يوفّر Compose عدة منحنيات:
LinearEasing- سرعة ثابتة (إحساس آلي).FastOutSlowInEasing- الإعداد الافتراضي في Material؛ بداية سريعة ونهاية ناعمة.LinearOutSlowInEasing- للعناصر التي تدخل الشاشة.FastOutLinearInEasing- للعناصر التي تغادر الشاشة.
يجعل اختيار التسهيل المناسب الحركة تبدو مقصودة بدلًا من أن تبدو آلية.
val progress by animateFloatAsState(
targetValue = if (done) 1f else 0f,
animationSpec = tween(500, easing = LinearOutSlowInEasing),
label = "progress"
)إعدادات النابض
بدلًا من استخدام مدة ثابتة، يمكنكم استخدام نابض يمنح الحركة إحساسًا فيزيائيًا وطبيعيًا. ويتحدد النابض بواسطة dampingRatio (مدى الارتداد) وstiffness (سرعة الحركة).
تتميز النوابض بأنها تتكيف بسلاسة حتى عندما تتغير القيمة المستهدفة أثناء الرسوم المتحركة.
val scale by animateFloatAsState(
targetValue = if (active) 1.2f else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
),
label = "scale"
)التفاعل عند الاكتمال
قد تحتاجون أحيانًا إلى تنفيذ إجراء عند اكتمال الرسوم المتحركة، مثل استدعاء دالة رد اتصال مرة واحدة عند وصول القيمة إلى هدفها. تقبل animate*AsState معامل finishedListener لهذا الغرض تحديدًا.
val alpha by animateFloatAsState(
targetValue = if (gone) 0f else 1f,
label = "alpha",
finishedListener = { finalValue ->
if (finalValue == 0f) onFullyHidden()
}
)تحريك عدة خصائص
يمكنكم الجمع بين عدة استدعاءات لـanimate*AsState لتحريك أكثر من خاصية في الوقت نفسه. يتتبع كل استدعاء قيمته الخاصة، وتنتج عنها مجتمعةً حركة أكثر ثراءً.
يتغير حجم شريحة اللون هنا ويتغير لونها عند تحديدها.
@Composable
fun SelectableChip(selected: Boolean, text: String) {
val scale by animateFloatAsState(if (selected) 1.1f else 1f, label = "s")
val bg by animateColorAsState(
if (selected) Color(0xFF3F51B5) else Color.LightGray,
label = "bg"
)
Box(
Modifier
.graphicsLayer { scaleX = scale; scaleY = scale }
.clip(RoundedCornerShape(16.dp))
.background(bg)
.padding(horizontal = 12.dp, vertical = 6.dp)
) { Text(text) }
}نابض في Kotlin الخالصة
لتكوين حدس حول ما يفعله النابض، إليكم محاكاة صغيرة باستخدام Kotlin الخالصة. فهي تدفع قيمة نحو هدف في كل خطوة مع قدر من التخميد، على نحو يشبه اقتراب نابض Compose من القيمة المستهدفة عبر عدة إطارات.
fun main() {
var value = 0f
val target = 100f
var velocity = 0f
val stiffness = 0.1f
val damping = 0.6f
repeat(6) { step ->
val force = (target - value) * stiffness
velocity = (velocity + force) * damping
value += velocity
println("step $step -> ${"%.1f".format(value)}")
}
}تحريك Int وOffset
تمتد هذه العائلة إلى ما يتجاوز القيم العشرية والألوان. استخدموا animateIntAsState للقيم الصحيحة مثل القيمة المستهدفة لعدد شارة، واستخدموا animateOffsetAsState أو animateIntOffsetAsState لتحريك الموضع ثنائي الأبعاد باستدعاء واحد.
تنزلق شريحة اللون هنا قطريًا عبر تحريك قيمة IntOffset.
@Composable
fun SlidingChip(moved: Boolean) {
val pos by animateIntOffsetAsState(
targetValue = if (moved) IntOffset(120, 60) else IntOffset.Zero,
label = "pos"
)
Box(
Modifier
.offset { pos }
.size(40.dp)
.background(Color.Magenta)
)
}تحقق سريع
تريدون تحريك عرض مربع بسلاسة، ويُقاس العرض بوحدة Dp، عند تغيّر قيمة منطقية. ما واجهة برمجة التطبيقات الأنسب؟
مراجعة: animate*AsState
عائلة animate*AsState هي خياركم الأساسي لتحريك قيمة واحدة:
- اختاروا المتغير المناسب للنوع:
animateFloatAsStateأوanimateDpAsStateأوanimateColorAsStateوغير ذلك. - حدّدوا قيمة
targetValue؛ وستتحرك النتيجة نحوها عند كل تغيير. - اضبطوا الحركة باستخدام
animationSpecمعtweenوالتسهيل أو معspring. - اجمعوا عدة استدعاءات لتحريك خصائص متعددة معًا.
- استخدموا
finishedListenerللتفاعل عند اكتمال الرسوم المتحركة.
التالي: تحريك ظهور المحتوى واختفائه باستخدام AnimatedVisibility والانتقالات.
الأسئلة الشائعة
هل درس «أساسيات animate*AsState» مجاني؟
نعم — نص درس «أساسيات animate*AsState» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Android Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Android Academy 4 دروس في المجموع.
ماذا ستتعلم في «أساسيات animate*AsState»؟
حرّك القيم المفردة بسلاسة تتمرن على Android Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Android Academy؟
لا تُشترط خبرة سابقة. Android Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «أساسيات animate*AsState»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Android Academy هذا؟
نعم. كل درس في Android Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- لماذا تهم الرسوم المتحركة
- أساسيات animate*AsState
- AnimatedVisibility والانتقالات
- حركات الإيماءات والنوابض