تحريك الحركة باستخدام الفيزياء الربيعية

تجربة طريقة ComposeAllowed
Jetpack Compose هي مجموعة أدوات واجهة المستخدم التي ننصح بها لنظام التشغيل Android. تعرَّف على كيفية استخدام "الصور المتحركة" في Compose.

مستندة إلى الفيزياء الحركة التي يتم تحريكها بالقوة. القوة الربيعية هي إحدى هذه القوى التي توجه والتفاعل والحركة. تتمتع القوة الربيعية بالخصائص التالية: التخميد والصلابة. في الرسوم المتحركة المستندة إلى النابض، تكون القيمة يتم حساب السرعة بناءً على القوة الربيعية المطبقة على كل الإطار.

إذا كنت تريد أن تنخفض الرسوم المتحركة في تطبيقك في اتجاه واحد فقط، ففكر في استخدام نظام يستند إلى رسم متحرك بدلاً من ذلك.

دورة حياة الصور المتحركة في فصل الربيع

في صورة متحركة مستندة إلى فصل الربيع، تم إنشاء علامة SpringForce تخصيص صلابة فصل الربيع ونسبة التخميد الموضع النهائي. عند بدء الرسم المتحرك، يتم تحديث قوة النابض قيمة الرسوم المتحركة والسرعة على كل إطار. تستمر الصورة المتحركة حتى تصل قوة النابض إلى التوازن.

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

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

إصدار الربيع
الشكل 1. تأثير إصدار الربيع

صمِّم صورًا متحركة مستوحاة من فصل الربيع

الخطوات العامة لإنشاء رسوم متحركة زنبركية لتطبيقك هي على النحو التالي:

تناقش الأقسام التالية الخطوات العامة لإنشاء ربيع الرسوم المتحركة بالتفصيل.

إضافة مكتبة الدعم

لاستخدام مكتبة الدعم المستنِدة إلى الفيزياء، يجب إضافة مكتبة الدعم إلى مشروعك. على النحو التالي:

  1. افتح ملف build.gradle الخاص بوحدة تطبيقك.
  2. أضِف مكتبة الدعم إلى القسم dependencies.

    Groovy

            dependencies {
                def dynamicanimation_version = '1.0.0'
                implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version"
            }
            

    Kotlin

            dependencies {
                val dynamicanimation_version = "1.0.0"
                implementation("androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version")
            }
            

    لعرض الإصدارات الحالية لهذه المكتبة، راجع معلومات حول الرسوم المتحركة الديناميكية في صفحة الإصدارات.

إنشاء صور متحركة بأسلوب الربيع

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

ملاحظة: عند إنشاء رسم متحرك في الربيع، تكون النتيجة يكون موضع الربيع اختياريًا. ومع ذلك، يجب تحديد قبل بدء الرسوم المتحركة.

Kotlin

val springAnim = findViewById<View>(R.id.imageView).let { img ->
    // Setting up a spring animation to animate the view’s translationY property with the final
    // spring position at 0.
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f)
}

Java

final View img = findViewById(R.id.imageView);
// Setting up a spring animation to animate the view’s translationY property with the final
// spring position at 0.
final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);

ويمكن للصور المتحركة المستندة إلى فصل الربيع تحريك العروض على الشاشة من خلال تغيير الخصائص الفعلية في كائنات العرض. تتوفر طرق العرض التالية في النظام:

  • ALPHA: يمثل شفافية ألفا في طريقة العرض. القيمة هي 1 (قيمة فارغة) حسب الافتراضي، بقيمة 0 تمثل الشفافية الكاملة (غير مرئية).
  • TRANSLATION_X, TRANSLATION_Y و TRANSLATION_Z: هذه تتحكّم الخصائص في مكان وضع العرض كدلتا من يساره الإحداثيات والإحداثيات العليا والارتفاع، والتي يتم تعيينها حسب تخطيطها .
  • ROTATION, ROTATION_X و ROTATION_Y: هذه تتحكم المواقع في الدوران ثنائي الأبعاد (الخاصية rotation) ثلاثي الأبعاد حول النقطة المحورية
  • SCROLL_X و SCROLL_Y: هذه تشير الخصائص إلى إزاحة التمرير للمصدر الأيسر والحافة العلوية بالبكسل. كما يشير أيضًا إلى موضع الصفحة ضمن عبارات البحث التمرير.
  • SCALE_X و SCALE_Y: هذه تتحكم الخصائص في التحجيم الثنائي الأبعاد لطريقة العرض حول النقطة المحورية.
  • X, Y و Z: هذه هي الخيارات الأساسية لوصف الموقع النهائي لطريقة العرض في .

تسجيل المستمعين

توفّر الفئة DynamicAnimation اثنتين المستمعين: OnAnimationUpdateListener وOnAnimationEndListener. يستمع هؤلاء المستمعون إلى التحديثات في الرسوم المتحركة مثلاً عند وجود تغير في قيمة الرسوم المتحركة وعندما تصل الرسوم المتحركة إلى النهاية.

OnAnimationUpdateListener

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

  1. الاتصال بـ "addUpdateListener()" وإرفاق المستمع بالرسوم المتحركة.

    ملاحظة: يجب تسجيل التحديث. المستمع قبل بدء الرسوم المتحركة. ومع ذلك، يجب أن يكون مستمع التحديث يتم تسجيله فقط إذا كنت بحاجة إلى تحديث كل إطار على قيمة الرسوم المتحركة التغييرات. تمنع أداة معالجة التحديث إمكانية حصول الصورة المتحركة على قيد التشغيل في مؤشر ترابط منفصل.

  2. إلغاء onAnimationUpdate() لإشعار المتصل بالتغيير في الكائن الحالي. تشير رسالة الأشكال البيانية يوضح نموذج التعليمات البرمجية التالي الاستخدام الإجمالي OnAnimationUpdateListener

Kotlin

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 ->
    SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y)
}
val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 ->
    SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y)
}

// Registering the update listener
anim1X.addUpdateListener { _, value, _ ->
    // Overriding the method to notify view2 about the change in the view1’s property.
    anim2X.animateToFinalPosition(value)
}

anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }

Java

// Creating two views to demonstrate the registration of the update listener.
final View view1 = findViewById(R.id.view1);
final View view2 = findViewById(R.id.view2);

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
final SpringAnimation anim1X = new SpringAnimation(view1,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim1Y = new SpringAnimation(view1,
    DynamicAnimation.TRANSLATION_Y);
final SpringAnimation anim2X = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim2Y = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_Y);

// Registering the update listener
anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

// Overriding the method to notify view2 about the change in the view1’s property.
    @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2X.animateToFinalPosition(value);
    }
});

anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

  @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2Y.animateToFinalPosition(value);
    }
});

OnAnimationEndListener

OnAnimationEndListener بنهاية الرسوم المتحركة. يمكنك إعداد خدمة المستمع لتلقّي رد الاتصال عندما تصل الرسوم المتحركة إلى التوازن أو يتم إلغاؤها. إلى لتسجيل المستمع، قم بتنفيذ الخطوات التالية:

  1. الاتصال بـ "addEndListener()" وإرفاق المستمع بالرسوم المتحركة.
  2. إلغاء onAnimationEnd() طريقة لتلقي إشعار عندما تصل الصورة المتحركة إلى التوازن أو يتم إلغاؤه.

إزالة المستمعين

لإيقاف تلقّي استدعاءات تحديث الصور المتحركة واستدعاءات إنهاء الصور المتحركة، الِاتِّصَالْ بِـ removeUpdateListener() وremoveEndListener() على التوالي.

ضبط قيمة بداية الصورة المتحركة

لضبط قيمة بداية الصورة المتحركة، استدعِ setStartValue() وتمرير القيمة الأولى للرسوم المتحركة. وإذا لم يتم ضبط حقل قيمة البداية، يستخدم الرسم المتحرك القيمة الحالية لخاصية الكائن كقيمة البداية.

ضبط نطاق قيمة الرسوم المتحركة

يمكنك ضبط الحد الأدنى والأقصى لقيم الرسوم المتحركة عندما تريد تقييد قيمة الخاصية إلى نطاق معين. كما أنه يساعد على التحكم في في حال تحريك الخصائص التي لها نطاق أساسي، مثل ألفا (من 0 إلى 1).

  • لضبط الحد الأدنى للقيمة، يمكنك طلب setMinValue() وتمرير القيمة الصغرى للسمة.
  • لضبط الحدّ الأقصى للقيمة، يمكنك طلب setMaxValue(). وتمرير القيمة القصوى للسمة.

تُرجع كلتا الطريقتين الرسوم المتحركة التي يتم تعيين القيمة لها.

ملاحظة: في حال ضبط قيمة البدء وكانت لديك وحددت نطاق قيمة الرسوم المتحركة، فتأكد من أن قيمة البدء ضمن الحد الأدنى ونطاق القيمة القصوى.

ضبط سرعة البدء

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

لضبط السرعة، استدعِ setStartVelocity() وتمرير السرعة بالبكسل في الثانية. تُرجع الطريقة دالة جسم القوة الربيعية الذي تم تعيين السرعة عليه.

ملاحظة: استخدِم GestureDetector.OnGestureListener أو طرق الفئة VelocityTracker للاسترداد والحساب سرعة إيماءات اللمس.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Compute velocity in the unit pixel/second
        vt.computeCurrentVelocity(1000)
        val velocity = vt.yVelocity
        setStartVelocity(velocity)
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Compute velocity in the unit pixel/second
vt.computeCurrentVelocity(1000);
float velocity = vt.getYVelocity();
anim.setStartVelocity(velocity);

جارٍ تحويل وحدات بكسل مستقلة الكثافة في الثانية إلى وحدات بكسل في الثانية

يجب أن تكون سرعة النابض بالبكسل في الثانية. إذا اخترت تقديم قيمة ثابتة كبداية للسرعة، أدخِل القيمة بوحدة بكسل مستقلة الكثافة في الثانية ثم نحولها إلى بكسل في الثانية. بالنسبة إلى الإحالة الناجحة، استخدِم applyDimension() من الفئة TypedValue. ارجع إلى الرمز النموذجي التالي:

Kotlin

val pixelPerSecond: Float =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)

Java

float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());

ضبط خصائص الربيع

تُحدِّد الفئة SpringForce قيمة getter. وطرق التثبيت لكل خاصية من خصائص النابض، مثل تخميد والصلابة. لضبط خصائص الربيع، من المهم إما لاسترداد كائن قوة النابض أو إنشاء قوة نبع مخصصة يمكنك تعيين الخصائص. لمزيد من المعلومات عن إنشاء تصميم قوة النابض، ارجع إلى إنشاء قوة نوابض مخصّصة .

نصيحة: أثناء استخدام طرق التحديد، يمكنك إنشاء سلسلة طريقة حيث إن جميع طرق الإعداد (setter) تُرجع قوة النابض الخاص بك.

نسبة التخميد

تصف نسبة التخميد انخفاضًا تدريجيًا في اهتزاز الربيع. من باستخدام نسبة التخميد، يمكنك تحديد مدى سرعة تضاؤل الاهتزازات من ارتداد إلى آخر. هناك أربع طرق مختلفة يمكنك من خلالها تثبيط الربيع:

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

لإضافة نسبة التخميد إلى الربيع، نفِّذ الخطوات التالية:

  1. الاتصال بـ "getSpring()" لاسترداد الربيع لإضافة نسبة التخميد.
  2. الاتصال بـ "setDampingRatio()" وتمرير نسبة التخميد التي تريد إضافتها إلى الربيع. تشير رسالة الأشكال البيانية نحصل على كائن قوة الربيع الذي تم تعيين نسبة التخميد عليه.

    ملاحظة: يجب أن تكون نسبة التخميد رقم غير سالب. إذا عيّنت نسبة التخميد على صفر، فسيعمل لا تصل أبدًا إلى موضع البقية. بمعنى آخر، هذا الجهاز يتأرجح إلى الأبد.

تتوفّر ثوابت نسبة التخميد التالية في النظام:

الشكل 2: ارتداد مرتفع

الشكل 3: ارتداد متوسط

الشكل 4: مستوى ارتداد منخفض

الشكل 5: ما مِن ارتداد

تم ضبط نسبة التخميد التلقائية على DAMPING_RATIO_MEDIUM_BOUNCY.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the damping ratio to create a low bouncing effect.
        spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the damping ratio to create a low bouncing effect.
anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
…

تيبس

تحدد الصلابة ثابت الزنا، الذي يقيس قوة ربيع يستخدم النوابض الصلب المزيد من القوة على الجسم المتصل عندما لا يكون الربيع في وضع الراحة. لإضافة الصلابة إلى الربيع، نفِّذ الخطوات التالية:

  1. الاتصال بـ "getSpring()" لاسترداد النبع وإضافة الصلابة.
  2. الاتصال بـ "setStiffness()" ومرر ومرر قيمة الصلابة التي تريد إضافتها إلى الربيع. تشير رسالة الأشكال البيانية ينتج عنها كائن قوة النوابض الذي يتم تعيين الصلابة عليه.

    ملاحظة: يجب أن تكون الصلابة رقم موجب.

تتوفر ثوابت الصلابة التالية في النظام:

الشكل 6: صلابة عالية

الشكل 7: صلابة متوسطة

الشكل 8: تيبس منخفض

الشكل 9: تيبس منخفض جدًا

تم ضبط درجة الصلابة التلقائية على STIFFNESS_MEDIUM.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the spring with a low stiffness.
        spring.stiffness = SpringForce.STIFFNESS_LOW
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the spring with a low stiffness.
anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);
…

أنشِئ قوة زنبركية مخصّصة

يمكنك إنشاء قوة نوابض مخصَّصة كبديل لاستخدام الإعدادات التلقائية قوة الربيع. تتيح لك قوة النوابض المخصّصة مشاركة قوة النابض المثال عبر العديد من الرسوم المتحركة في الربيع. بعد إنشاء فصل الربيع يمكنك تعيين خصائص مثل نسبة التخميد والصلابة.

  1. أنشئ عنصر SpringForce.

    SpringForce force = new SpringForce();

  2. عليك تعيين السمات من خلال استدعاء الطرق المعنية. يمكنك أيضًا لإنشاء سلسلة طريقة.

    force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);

  3. الاتصال بـ "setSpring()" لتعيين النابض بالحركة.

    setSpring(force);

بدء الصورة المتحركة

هناك طريقتان يمكنك من خلالهما بدء رسم متحرك ربيعي: من خلال استدعاء start() أو من خلال طلب animateToFinalPosition() . يجب طلب كلتا الطريقتين في سلسلة التعليمات الرئيسية.

animateToFinalPosition() تؤدي إلى تنفيذ مهمتين وهما:

  • لضبط الموضع النهائي للزنبرك.
  • يؤدي إلى بدء تشغيل الصورة المتحركة في حال لم تكن قد بدأت.

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

ويوضّح الشكل 10 رسمًا متحركًا لسلسلة زنبركية متسلسلة، حيث تظهر صورة متحركة على طريقة عرض أخرى.

عرض توضيحي لسلسلة زنبركية
الشكل 10 عرض توضيحي لسلسلة زنبركية

لاستخدام animateToFinalPosition() استدعِ animateToFinalPosition() واجتياز الموضع المتبقي من الربيع. يمكنك أيضًا تعيين بقية موضع النابض عن طريق استدعاء setFinalPosition() .

تُستخدم الطريقة start() عدم ضبط قيمة الخاصية على قيمة البداية على الفور. الموقع تتغير القيمة مع كل نبض الصورة المتحركة، والتي تحدث قبل ممر الرسم. ونتيجةً لذلك، تظهر التغييرات في الإطار التالي، كما لو يتم تعيين القيم على الفور.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Starting the animation
        start()
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Starting the animation
anim.start();
…

إلغاء الصورة المتحركة

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

هناك طريقتان يمكنك استخدامهما لإنهاء الحركة. الطريقة cancel() لإنهاء الرسم المتحرك بالقيمة حيثما تكون. تشير رسالة الأشكال البيانية طريقة واحدة (skipToEnd()) يتخطى الرسم المتحرك إلى القيمة النهائية ثم يُنهيه.

قبل إنهاء الرسوم المتحركة، من المهم التحقق أولاً من حالة الربيع. إذا كانت الحالة غير ثابتة، فلن تتمكن الصورة المتحركة أبدًا موضع الباقي. للتحقق من حالة الربيع، اتصل طريقة canSkipToEnd(). في حال حذف تم تجفيف الربيع، وتعرض الطريقة true، وإلا false

بعد معرفة حالة الربيع، يمكنك إنهاء الصورة المتحركة عن طريق باستخدام skipToEnd() أو طريقة طريقة cancel(). تشير رسالة الأشكال البيانية طريقة واحدة (cancel()) يجب طلبها في سلسلة المحادثات الرئيسية فقط.

ملاحظة: بشكل عام، أسباب إجراء skipToEnd() القفز البصري.