안드로이드(Android)

특정 뷰에 애니메이션 적용하는 방법 그리고 적용 후 GONE 혹은 INVISIBLE이 안될 때

Gnow 2023. 8. 28. 15:29
728x90
반응형

이번에는 지난번에 만들었던 fab(floatingButton)를 다루다가 알게 된 점을 정리하려고 한다.

이번 글은 floatingButton 외에도 애니메이션을 적용하는 경우에 사용할 수 있다.

 

floatingButton의 경우 버튼을 누르면 다른 버튼들이 위로 나오거나 회전하는 등의 애니메이션이 적용되어 있는 경우가 많다.


우선 이러한 애니메이션을 만드는 경우는 다음과 같다. (visibility 적용하는 방법은 아래에 있다.)

 

PART 1. 애니메이션 만들기

 

 

 

프로젝트 구조를 Android로 했을때 app > res 안에 anim 이라는

폴더를 만들고 원하는 애니메이션.xml 파일을 넣으면 된다.

 

 

 

 

 

현재 내가 쓰고 있는 애니메이션 종류는 여러 종류 중 'rotate(회전)' 과 scale(사이즈 조절), 'alpha(밝기 조절)'를 사용했다.

이 외에도 애니메이션과 관련된 코드는 매우 다양하니 필요한 건 더 찾아보면 된다.

 

< roate(회전) 애니메이션 코드 - 위에서부터 4번째 맨 아래 버튼에 적용 >

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <rotate
        android:fromDegrees="0"
        android:toDegrees="45"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"/>

</set>

이 코드는 이미지를 시계 방향으로 회전하게 만드는 애니메이션으로

위에서부터 설명하자면 set을 통해 그룹화하여 scale과 alpha를 동시에 실행할 수 있게 해준다.

그리고 android:fillAfter는 애니메이션이 종료되어도 유지할 것인지를 다루는 것으로 true를 하면 유지가 된다.

 

- rotate 애니메이션은 회전을 다루는 애니메이션이다.

 

android:fromDegress="0"은 처음 각도를 나타낸다. 0이므로 원상태이다.

android:toDegress="45"는 마지막 각도를 나타낸다. 45이므로 45º 만큼 회전을 시키는 것이다.

android:pivotX="50%"는 회전하는 X축 기준점으로 50%로 지정하였으니 X축으로 이미지의 가운데를 기준으로 회전하게 된다. 

 

android:interpolator="@android:anim/linear_interpolator" 는 애니메이션의 처음과 마지막 사이의 감속, 가속 등과 같은 효과를 다루는 코드이다. 여기서는 linear_interpolator로 지정하였다. 이것은 동일한 속도로 처음에서 마지막 애니메이션을 하라는 것이다.

 

android:duration="300"은 애니메이션의 총 시간을 얼마의 속도로 할 것인가를 다루는 것이다. 숫자가 작을 수록 애니메이션이 짧아진다.

 

다시 원래대로 회전하는 애니메이션은 다음과 같다.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false">

    <rotate
        android:fromDegrees="45"
        android:toDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"/>

</set>

여기서 잘보면 android:fillAfter="false"로 한걸 볼 수 있는데 이 애니메이션의 경우 원상태로 돌리는 것이기 때문에 애니메이션 종료 후에 유지할 필요가 없기에 false로 지정하였다.

 

이것을 false로 해야하는 이유는 아래 PART3에서 얘기하게 된다.


<scale(사이즈 조절) & alpah(밝기 조절) 코드 - 위에서부터 1 ~ 3번째 버튼에 적용 >

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <scale
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:toXScale="1.0"
        android:toYScale="1.0"
        android:pivotY="50%"
        android:pivotX="50%"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"
        />

    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"/>

</set>

이 코드는 나타나게 하는 애니메이션으로

 

- scale 애니메이션은 축소, 확대를 다루는 애니메이션이다.

 

fromXScale = "0.0"은 처음 X의 크기를 나타내는 값으로 0.0으로 지정하였으니 크기가 0으로 안보이는 상태이다.

 

toXScale="1.0"은 마지막 X의 크기를 나타내는 값으로 1.0으로 지정하였으니 원래 크기로 보이는 상태이다.

 

pivotY="50%"는 애니메이션이 작용할 Y위치이다. 50%으로하였으니 Y축으로 가운데에서 애니메이션이 작용한다.

즉 이미지의 가운데에서 점점 커지는 효과가 나오는 것이다.

 

android:interpolator="@android:anim/linear_interpolator" 는 애니메이션의 처음과 마지막 사이의 감속, 가속 등과 같은 효과를 다루는 코드이다. 여기서는 linear_interpolator로 지정하였다. 이것은 동일한 속도로 처음에서 마지막 애니메이션을 하라는 것이다.

 

android:duration="300"은 애니메이션의 총 시간을 얼마의 속도로 할 것인가를 다루는 것이다. 숫자가 작을 수록 애니메이션이 짧아진다.

 

- alpha 애니메이션은 투명도를 다루는 애니메이션이다.

 

android:fromAlpha="0.0"은 처음 투명도로 0.0은 0% 이니 안보이게 된다. 위의 scale 애니메이션의 크기를 0으로 하는 것과 같은 시각적 효과를 나타낼 수 있다.

 

android:toAlpha="1.0"은 100%으로 원래 이미지의 투명도로 보인다.

 

android:interpolator="@android:anim/linear_interpolator" android:duration="300" 은 scale 애니메이션과 동일하다.

 

사라지게 하는 애니메이션은 아래와 같다.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:toXScale="0.0"
        android:toYScale="0.0"
        android:pivotY="50%"
        android:pivotX="50%"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"
        />

    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0.0"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"/>

</set>

 

단순히 사라졌다 나타나게만 하고 싶다면 scale이나 alpha 애니메이션 둘중 하나만 사용해서  된다.


PART 2. 애니메이션 적용하기

다음과 같이 activity_main.xml에 4개의 버튼을 만들어 놨다.

나의 경우 맨 아래 노란색 버튼인(default_upload) 버튼에는 rotate를 나머지 3개는 scale과 alpha를 적용했다.

3개가 빈 칸처럼 보이는 것은 visibility를 invisible로 해서 안보이는 상태이다.

맨 아래 버튼을 누르면 나머지 3개의 버튼이 보이도록하기 위해 default_upload 버튼에 setOnClickListener로 작동하도록 작성하였다.

default_upload.setOnClickListener{

            if(isOpen) closeFab()  // floating button 닫는 함수

            else{   // floating button 이 닫혀 있을 경우
                openFab()
                }
}

만약 버튼들이 열려 있는 상태에서 노란색 버튼을 누르면 closeFab() 함수를 통해 닫아지는 효과를 주고 닫혀있는 상태에서 누른다면 열리는 효과를 주려했다. isOpen은 닫힘 열림을 판단하기 위한 변수로 true라면 열려 있는 것이다.

 

 


openFab 함수는 아래와 같다.

/** floatingButton 을 펼칠 때 사용 하는 함수 */
    private fun openFab() {

        // 맨 아래 버튼의 시계 방향 회전 애니메이션
        val rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate_clockwise)
        default_upload.startAnimation(rotateAnimation)

        // 나머지 버튼이 나타나게하는 애니메이션
        val fabOpen = AnimationUtils.loadAnimation(this, R.anim.fab_open)

        image_upload.startAnimation(fabOpen)
        camera_upload.startAnimation(fabOpen)
        file_upload.startAnimation(fabOpen)

        image_upload.isClickable
        camera_upload.isClickable
        file_upload.isClickable

        isOpen = true
    }

AnimationUtils.loadAnimation을 통해 원하는 애니메이션을 불러오고

버튼id.startAnimation(불러온 애니메이션) 형식으로 적용하면 된다.

rotate_clockwise는 시계 방향으로 45º 회전하게 하는 애니메이션이다.

 

나머지 버튼들도 같게 해주면 된다.

image_upload.isClickable 은 클릭할 수 있게 만드는 코드이다.

isOpen은 true를 통해 열렸다는 것을 나타내는 변수이다.

 

 

 

닫는 closeFab 함수는 아래와 같다.

    /** floatingButton 을 닫을 때 사용 하는 함수 */

    private fun closeFab() {

        // 맨 아래 버튼의 반시계 방향 회전 애니메이션
        // default_upload 의 GONE 을 위해 android:fillAfter="false" 로 지정 즉, 애니메이션 끝나면 유지 X
        val rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate_anticlockwise)
        default_upload.startAnimation(rotateAnimation)

        // 나머지 버튼이 사라지게하는 애니메이션

        val fabClose = AnimationUtils.loadAnimation(this, R.anim.fab_close)

        image_upload.startAnimation(fabClose)
        camera_upload.startAnimation(fabClose)
        file_upload.startAnimation(fabClose)

        isOpen = false

    }

 

이러한 방식으로 애니메이션을 적용하면 된다.


PART 3. 애니메이션을 적용한 후 버튼을 사라지게하려했으나 안사라지는 경우

 

이 후에 다른 프래그먼트로 이동하면 floatingButton이 사라지게 하고 싶었다.

default_upload.visibility = View.GONE   // floating 버튼 안 보이게

View.GONE을 하면 버튼을 사라지게 할 수 있다.

 

버튼을 누르지 않고 프래그먼트를 이동할 때는 사라졌으나 어째서인지 버튼을 눌러 펼치고 난 뒤에는 버튼이 사라지지 않았다.

 

이러한 저러한 시도를 해보던 와중 애니메이션 코드가 문제였던걸 알 수 있었다.

그것은 위에서 말했던 android:fillAfter="true" 부분이였다. true로 되어 있으면 애니메이션이 끝나도 유지되어 적용된 상태 그대로 있다. 그래서 View.GONE을 이용해 사라지게 하려해도 그대로 있었던 것이다.

 

false를 통해 애니메이션이 유지가 안된다해도 rotate_anticlockwise가 반시계로 돌리는 애니메이션이니까 이미지는 시계 방향으로 회전하기 전 그대로 인것이다.

 

- 반시계로 돌리는 애니메이션

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false">

    <rotate
        android:fromDegrees="45"
        android:toDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="300"/>

</set>

위처럼 한다면 View.GONE이 잘 적용되는 것을 볼 수 있다.

728x90
반응형