إظهار العرض أو إخفاؤه باستخدام الصور المتحركة

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

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

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

إنشاء صورة متحركة لتلاشي متقاطع

تتلاشى صورة متلاشية View أو ViewGroup تدريجيًا مع إضفاء طابع متلاشٍ عليها، وتُعرف أيضًا باسم التلاشي. هذه الصورة المتحركة مفيدة في الحالات التي تريد فيها تبديل المحتوى أو طرق العرض في تطبيقك. تستخدم الصورة المتحركة المتداخلة التي تظهر هنا ViewPropertyAnimator، المتاحة لنظام التشغيل Android 3.1 (مستوى واجهة برمجة التطبيقات 12) والإصدارات الأحدث.

فيما يلي مثال على التلاشي المتقاطع من مؤشر التقدم إلى المحتوى النصي:

الشكل 1. صورة متحرّكة لتلاشي متقاطع

إنشاء طرق العرض

أنشئ طريقتَي العرض اللذَين تريد تقاطعهما. ينشئ المثال التالي مؤشر تقدم وعرض نص قابل للتمرير:

<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>

إعداد الحركة المتقاطعة

لإعداد الحركة المتقاطعة، قم بما يلي:

  1. قم بإنشاء متغيرات الأعضاء لطرق العرض التي تريد أن يتداخل مع بعضها. تحتاج إلى هذه المراجع لاحقًا عند تعديل طرق العرض أثناء الرسوم المتحركة.
  2. ضبط مستوى رؤية العرض الذي يتم التلاشي للداخل إلى GONE. يمنع هذا العرض من استخدام مساحة التخطيط ويحذفه من حسابات التخطيط، مما يؤدي إلى تسريع المعالجة
  3. تخزين خاصية نظام 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);
    }
    ...
}

تلاشٍ متقاطع من العروض

وعند إعداد طرق العرض بشكلٍ صحيح، يمكنك جعل طرق العرض متداخلة من خلال إجراء ما يلي:

  1. بالنسبة إلى العرض الذي يتلاشى للداخل، اضبط قيمة ألفا على 0 ومستوى الظهور على VISIBLE من الإعداد الأوليّ GONE. يؤدي هذا إلى جعل العرض مرئي ولكن شفاف.
  2. بالنسبة للعرض الذي يتلاشى للداخل، قم بتحريك قيمة ألفا من 0 إلى 1. بالنسبة للعرض الذي يتلاشى، قم بتحريك قيمة ألفا من 1 إلى 0.
  3. باستخدام 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.

في ما يلي الشكل الذي يبدو عليه قلب البطاقة:

الشكل 2. صورة متحركة لقلب البطاقات

إنشاء كائنات الرسوم المتحركة

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

card_flip_left_in.xml

<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>

card_flip_left_out.xml

<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>

card_flip_right_in.xml

<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>

card_flip_right_out.xml

<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 عند اكتمال الصورة المتحركة.

مراجع إضافية