أثناء استخدام تطبيقك، تظهر معلومات جديدة على الشاشة ومعلومات قديمة. ستتم إزالة المعلومات. يمكن تغيير ما يظهر على الشاشة فورًا وقد يفوت المستخدمين أي محتوى جديد يظهر فجأة. الصور المتحركة بطيئة التغييرات ورسم عين المستخدم بالحركة بحيث تتم أكثر وضوحًا.
هناك ثلاث صور متحركة شائعة يمكنك استخدامها لإظهار طريقة عرض أو إخفائها: إظهار والرسوم المتحركة، والرسوم المتحركة ذات التلاشي المتقاطع، والرسوم المتحركة في Cardflip.
إنشاء رسوم متحركة متشابكة
تظهر صورة متحركة ذات تلاشٍ متقاطع، وتُعرف أيضًا باسم التلاشي، وتتلاشى تدريجيًا
واحد View
أو
ViewGroup
في الوقت نفسه
يتلاشى من جديد. هذه الصورة المتحركة مفيدة في المواقف التي تريد فيها
تبديل المحتوى أو طرق العرض في التطبيق تستخدم الرسوم المتحركة للتلاشي المتقاطع الموضحة هنا
ViewPropertyAnimator
،
متوفّر لنظام التشغيل Android 3.1 (المستوى 12 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
في ما يلي مثال على تلاشٍ متقاطع من مؤشر التقدم إلى محتوى نصي:
إنشاء طرق العرض
أنشئ طريقتَي العرض اللتين تريد تطبيقهما على الرسائل الإلكترونية التي تريد تطبيق التغييرات عليها. ينشئ المثال التالي مؤشر تقدّم وعرض نص قابل للتمرير:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:padding="16dp" />
</ScrollView>
<ProgressBar android:id="@+id/loading_spinner"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
إعداد الرسوم المتحركة للتلاشي المتقاطع
لإعداد الرسوم المتحركة للتلاشي المتقاطع، قم بما يلي:
- أنشِئ متغيّرات للأعضاء لطرق العرض التي تريد أن تظهر التغييرات بشكلٍ متداخل. أنت بحاجة إلى هذه المراجع لاحقًا عند تعديل طرق العرض أثناء الرسوم المتحركة.
- تعيين مستوى رؤية العرض الذي تتلاشى فيه
GONE
يمنع هذا الإجراء العرض من استخدام مساحة التخطيط وحذفها من حسابات التخطيط، والتي تزيد من سرعتك جارٍ المعالجة - تخزين
config_shortAnimTime
مؤقتًا خاصية نظام في متغير عضو. تحدِّد هذه السمة القيمة "القصيرة" العادية. مدة الرسوم المتحركة. هذه المدة مثالية للرسوم المتحركة الدقيقة أو والرسوم المتحركة التي تحدث بشكل متكرر.config_longAnimTime
أوconfig_mediumAnimTime
تتوفر أيضًا.
في ما يلي مثال على استخدام التنسيق من مقتطف الرمز السابق عرض محتوى النشاط:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_crossfade) contentView = findViewById(R.id.content) loadingView = findViewById(R.id.loading_spinner) // Initially hide the content view. contentView.visibility = View.GONE // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) } ... }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crossfade); contentView = findViewById(R.id.content); loadingView = findViewById(R.id.loading_spinner); // Initially hide the content view. contentView.setVisibility(View.GONE); // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = getResources().getInteger( android.R.integer.config_shortAnimTime); } ... }
تلاشٍ متقاطع طرق العرض
عند إعداد طرق العرض بشكل صحيح، يمكنك تلاشيها بشكل متداخل عن طريق إجراء ما يلي:
- بالنسبة إلى الملف الشخصي الذي يتلاشى، اضبط قيمة ألفا على 0 ومستوى الرؤية
إلى
VISIBLE
منذ البداية الإعدادGONE
. وهذا يجعل طريقة العرض مرئية ولكنها شفافة. - بالنسبة إلى طريقة العرض التي تتلاشى، حرِّك قيمة ألفا من 0 إلى 1. بالنسبة إلى طريقة عرض تتلاشى، قم بتحريك قيمة ألفا من 1 إلى 0.
- استخدام
onAnimationEnd()
فيAnimator.AnimatorListener
, ضبط مستوى رؤية العرض الذي يتلاشى علىGONE
. على الرغم من أن قيمة ألفا هي 0، ويؤدي ضبط مستوى رؤية العرض علىGONE
إلى منع المشاهدة من استخدام مساحة التخطيط وحذفها من حسابات التخطيط، والتي تزيد من سرعتك قيد المعالجة.
توضح الطريقة التالية مثالاً على كيفية القيام بذلك:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... private fun crossfade() { contentView.apply { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. alpha = 0f visibility = View.VISIBLE // Animate the content view to 100% opacity and clear any animation // listener set on the view. animate() .alpha(1f) .setDuration(shortAnimationDuration.toLong()) .setListener(null) } // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration.toLong()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { loadingView.visibility = View.GONE } }) } }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... private void crossfade() { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. contentView.setAlpha(0f); contentView.setVisibility(View.VISIBLE); // Animate the content view to 100% opacity and clear any animation // listener set on the view. contentView.animate() .alpha(1f) .setDuration(shortAnimationDuration) .setListener(null); // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loadingView.setVisibility(View.GONE); } }); } }
إنشاء صورة متحركة لقلب البطاقة
يؤدي قلب البطاقات إلى التبديل بين طرق عرض المحتوى من خلال عرض صورة متحركة تحاكي صورة متحركة.
تقلب بطاقة. تستخدم الصورة المتحركة لقلب البطاقة الموضحة هنا
FragmentTransaction
في ما يلي طريقة عمل قلب البطاقة:
إنشاء كائنات الرسوم المتحركة
لإنشاء رسم متحرك بقلب البطاقة، تحتاج إلى أربعة مؤثرات حركية. اثنان من مصممي الرسوم المتحركة للإشارة إلى الوقت الذي تتحرك فيه الجهة الأمامية من البطاقة وإلى اليسار وعند تحريكها في الداخل ومن اليسار. يستخدم مقطعا الصور المتحركة الآخران الجزء الخلفي من البطاقة ويتحرك في الداخل ومن اليمين وعند تحركه للخارج وإلى اليمين.
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
إنشاء طرق العرض
كل جانب من البطاقة هو تخطيط منفصل يمكن أن يحتوي على أي محتوى التي تريد استخدامها، مثل عرضَي نص أو صورتين أو أي مجموعة من طرق العرض للتبديل بينهما. استخدِم التنسيقَين في الأجزاء التي تريد تحريكها لاحقًا. تشير رسالة الأشكال البيانية يؤدي التنسيق التالي إلى إنشاء جانب واحد من البطاقة يعرض النص:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
android:padding="16dp"
android:gravity="bottom">
<TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" />
<TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" />
</LinearLayout>
وينشئ التخطيط التالي الجانب الآخر من البطاقة، والذي يعرض
ImageView
:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" />
إنشاء الأجزاء
إنشاء فئات مجزّأة للواجهة الأمامية والخلفية من البطاقة في الجزء
الفئات، تُرجع التخطيطات التي قمت بإنشائها
onCreateView()
. يمكنك بعد ذلك إنشاء مثيلات لهذا الجزء في النشاط الرئيسي.
التي تريد عرض البطاقة فيها
يُظهر المثال التالي فئات تجزئة متداخلة داخل النشاط الرئيسي التي تستخدمها:
Kotlin
class CardFlipActivity : FragmentActivity() { ... /** * A fragment representing the front of the card. */ class CardFrontFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_front, container, false) } /** * A fragment representing the back of the card. */ class CardBackFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_back, container, false) } }
Java
public class CardFlipActivity extends FragmentActivity { ... /** * A fragment representing the front of the card. */ public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } } /** * A fragment representing the back of the card. */ public class CardBackFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } } }
تحريك قلب البطاقة
عرض الأجزاء داخل نشاط رئيسي. للقيام بذلك، قم بإنشاء التخطيط
على نشاطك. ينشئ المثال التالي
FrameLayout
التي يمكنك إضافتها
الأجزاء إلى وقت التشغيل:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
في رمز النشاط، عيِّن عرض المحتوى ليكون التنسيق الذي تنشئه. من الممارسات الجيدة عرض جزء افتراضي عند إنشاء النشاط. تشير رسالة الأشكال البيانية يوضح النشاط التالي كيفية عرض واجهة البطاقة من خلال الافتراضي:
Kotlin
class CardFlipActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_activity_card_flip) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.container, CardFrontFragment()) .commit() } } ... }
Java
public class CardFlipActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_activity_card_flip); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.container, new CardFrontFragment()) .commit(); } } ... }
يظهر الجزء الأمامي من البطاقة في الجهة الخلفية من البطاقة قلب الرسوم المتحركة في الوقت المناسب. أنشئ طريقة لإظهار الجانب الآخر من البطاقة التي تنفذ الأشياء التالية:
- لضبط الصور المتحركة المخصّصة التي أنشأتها لانتقالات الأجزاء.
- لاستبدال الجزء المعروض بجزء جديد وتحريك هذا الحدث مع الرسوم المتحركة المخصصة التي أنشأتها.
- لإضافة الجزء المعروض سابقًا إلى مكدس الجزء الخلفي، بحيث عندما ينقر المستخدم فوق الزر "رجوع"، وتقلب البطاقة مرة أخرى.
Kotlin
class CardFlipActivity : FragmentActivity() { ... private fun flipCard() { if (showingBack) { supportFragmentManager.popBackStack() return } // Flip to the back. showingBack = true // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. supportFragmentManager.beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is tapped. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out ) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, CardBackFragment()) // Add this transaction to the back stack, letting users press // the Back button to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit() } }
Java
public class CardFlipActivity extends FragmentActivity { ... private void flipCard() { if (showingBack) { getSupportFragmentManager().popBackStack(); return; } // Flip to the back. showingBack = true; // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. getSupportFragmentManager() .beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is pressed. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, new CardBackFragment()) // Add this transaction to the back stack, letting users press // Back to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit(); } }
إنشاء صورة متحركة دائرية للاكتشاف
توفر الرسوم المتحركة المعروضة للمستخدمين استمرارية مرئية عند إظهار مجموعة أو إخفائها
من عناصر واجهة المستخدم. تشير رسالة الأشكال البيانية
ViewAnimationUtils.createCircularReveal()
تحريك دائرة الاقتصاص لإظهار أو إخفاء طريقة عرض. هذا النمط
تتوفر في العنصر
صف واحد (ViewAnimationUtils
)
متوفّر لنظام التشغيل Android 5.0 (المستوى 21 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
في ما يلي مثال يوضّح كيفية الكشف عن طريقة عرض غير مرئية سابقًا:
Kotlin
// A previously invisible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the final radius for the clipping circle. val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animator for this view. The start radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius) // Make the view visible and start the animation. myView.visibility = View.VISIBLE anim.start() } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.visibility = View.INVISIBLE }
Java
// A previously invisible view. View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the final radius for the clipping circle. float finalRadius = (float) Math.hypot(cx, cy); // Create the animator for this view. The start radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius); // Make the view visible and start the animation. myView.setVisibility(View.VISIBLE); anim.start(); } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.setVisibility(View.INVISIBLE); }
تستغرق الحركة ViewAnimationUtils.createCircularReveal()
خمس معلمات.
المعلمة الأولى هي العرض الذي تريد إخفاؤه أو إظهاره على الشاشة. تشير رسالة الأشكال البيانية
المعلمتان التاليتان هما الإحداثيتان X وY لمركز القاطع
دَائِرَة. وعادةً ما يكون هذا هو مركز العرض، ولكن يمكنك أيضًا استخدام
بحيث ينقر المستخدم فوقها بحيث تبدأ الرسوم المتحركة من المكان الذي يحدده. تشير رسالة الأشكال البيانية
المعلمة الرابعة هي نصف قطر البدء لدائرة الاقتصاص.
وفي المثال السابق، يتم تعيين نصف القطر الأولي إلى صفر بحيث الذي يتم عرضه مخفيًا بواسطة الدائرة. المعلمة الأخيرة هي نصف القطر النهائي من الدائرة. عند عرض طريقة عرض، اجعل نصف القطر النهائي أكبر من بحيث يمكن كشف العرض بالكامل قبل انتهاء الرسم المتحرك.
لإخفاء طريقة عرض كانت مرئية من قبل، يمكنك اتباع الخطوات التالية:
Kotlin
// A previously visible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the initial radius for the clipping circle. val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animation. The final radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f) // Make the view invisible when the animation is done. anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) myView.visibility = View.INVISIBLE } }) // Start the animation. anim.start() } else { // Set the view to visible without a circular reveal animation below // Android 5.0. myView.visibility = View.VISIBLE }
Java
// A previously visible view. final View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the initial radius for the clipping circle. float initialRadius = (float) Math.hypot(cx, cy); // Create the animation. The final radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f); // Make the view invisible when the animation is done. anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); myView.setVisibility(View.INVISIBLE); } }); // Start the animation. anim.start(); } else { // Set the view to visible without a circular reveal animation below Android // 5.0. myView.setVisibility(View.VISIBLE); }
وفي هذه الحالة، يتم تعيين نصف القطر الأولي لدائرة القطع ليكون بحجم
العرض بحيث يكون العرض مرئيًا قبل بدء الرسم المتحرك. المباراة النهائية
يتم تعيين نصف القطر على صفر بحيث يتم إخفاء العرض عند انتهاء الرسم المتحرك.
إضافة أداة استماع إلى الصورة المتحركة بحيث يمكن تعيين مستوى رؤية العرض على
INVISIBLE
عند تشغيل الصورة المتحركة
تكتمل.
مصادر إضافية
- صورة متحركة باستخدام Jetpack Compose
- الإيماءات باستخدام Jetpack Compose