애니메이션으로 뷰 표시 또는 숨기기

Compose 방식 사용해 보기
Jetpack Compose는 Android의 권장 UI 도구 키트입니다. Compose에서 애니메이션을 사용하는 방법을 알아보세요.

앱이 사용되는 동안 새 정보가 화면에 표시되고 이전 정보는 삭제됩니다. 화면에 표시되는 내용을 즉시 변경하면 부자연스럽게 보일 수 있으며 사용자가 갑자기 나타나는 새 콘텐츠를 놓칠 수 있습니다. 애니메이션은 변화 속도를 늦추고 움직임으로 사용자의 시선을 끌어 업데이트를 더 분명하게 알 수 있도록 합니다.

뷰를 표시하거나 숨기는 데 사용할 수 있는 일반적인 애니메이션에는 표시 애니메이션, 크로스페이드 애니메이션, 카드플립 애니메이션의 세 가지가 있습니다.

크로스페이드 애니메이션 만들기

크로스페이드 애니메이션(디졸브라고도 함)은 하나의 View 또는 ViewGroup을 점진적으로 페이드 아웃하는 동시에 다른 View 또는 ViewGroup을 페이드 인합니다. 이 애니메이션은 앱에서 콘텐츠 또는 뷰를 전환하려는 경우 유용합니다. 여기에 표시된 크로스페이드 애니메이션에서는 ViewPropertyAnimator, Android 3.1 (API 수준 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_longAnimTimeconfig_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)
    }
    ...
}

자바

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. 페이드 인되는 뷰의 알파 값을 초기 설정인 GONE에서 0으로 설정하고 가시성 을 VISIBLE로 설정합니다. 이렇게 하면 뷰가 표시되기는 하지만 투명한 상태입니다.
  2. 페이드 인되는 뷰는 알파 값을 0에서 1로 애니메이션합니다. 페이드 아웃되는 뷰는 알파 값을 1에서 0으로 애니메이션합니다.
  3. Animator.AnimatorListener에서 onAnimationEnd()를 사용하여 페이드 아웃된 뷰의 가시성을 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
                    }
                })
    }
}

자바

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. 카드플립 애니메이션

애니메이터 객체 만들기

카드플립 애니메이션을 만들려면 애니메이터가 4개 필요합니다. 두 개의 애니메이터는 카드의 앞면이 왼쪽으로 애니메이션 아웃될 때와 왼쪽에서 애니메이션 인될 때 사용됩니다. 나머지 두 개의 애니메이터는 카드의 뒷면이 오른쪽에서 애니메이션 인될 때와 오른쪽으로 애니메이션 아웃될 때 사용됩니다.

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

자바

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()
        }
    }
    ...
}

자바

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()
    }
}

자바

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();
    }
}

회전 표시 애니메이션 만들기

표시 애니메이션은 UI 요소 그룹을 표시하거나 숨길 때 사용자에게 시각적 연속성을 제공합니다. ViewAnimationUtils.createCircularReveal() 메서드를 사용하면 뷰를 표시하거나 숨기도록 클리핑 서클을 애니메이션할 수 있습니다. 이 애니메이션은 Android 5.0 (API 수준 21) 이상에서 사용할 수 있는 ViewAnimationUtils 클래스에서 제공됩니다.

다음은 이전에 보이지 않았던 뷰를 표시하는 방법을 보여주는 예제입니다.

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
}

자바

// 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() 애니메이션에는 5개의 매개변수가 사용됩니다. 첫 번째 매개변수는 화면에서 숨기거나 표시하려는 뷰입니다. 그다음 두 개의 매개변수는 클리핑 서클 중심에 대한 X, Y 좌표입니다. 일반적으로 이것이 뷰의 중심이 되지만, 사용자가 탭한 지점도 사용할 수 있습니다. 이 경우 사용자가 선택한 위치에서 애니메이션이 시작됩니다. 네 번째 매개변수는 클리핑 서클의 시작 반지름입니다.

이전 예제에서 초기 반지름이 0으로 설정되었기 때문에 뷰가 서클에 숨겨집니다. 마지막 매개변수는 서클의 최종 반지름입니다. 뷰를 표시할 때 최종 반지름이 뷰 자체보다 커야 합니다. 그래야 애니메이션이 끝나기 전에 뷰가 완전히 표시됩니다.

이전에 표시된 뷰를 숨기려면 다음을 실행합니다.

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
}

자바

// 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);
}

이 경우 클리핑 서클의 초기 반지름 크기가 뷰와 같도록 설정되었기 때문에 애니메이션이 시작되기 전에 뷰가 표시됩니다. 최종 반지름이 0으로 설정되어 애니메이션이 끝나면 뷰가 숨겨집니다. 애니메이션이 완료되면 뷰의 가시성을 INVISIBLE 설정할 수 있도록 애니메이션에 리스너를 추가합니다.

추가 리소스