자주 사용되는 UI Animation


Android에서 애니메이션을 사용하려면 디자이너가 제공하는 GIF 파일을 다운로드해야 합니다.

로티 도서관사용되었습니다.

하지만 GIF ​​파일이 없더라도 안드로이드에서 애니메이션 효과를 구현할 수 있는 방법은 많기 때문에 연구해보고 싶었습니다.

Droid Knights 2020 – Android UI에서 애니메이션 전송나는 .

애니메이션 로드 중

: ProgressBar + Drawable을 이용하여 로딩 애니메이션을 만들어 봅시다.

  1. 로드 시 애니메이션을 적용하려면 먼저 드로어블 파일을 생성해야 합니다.

res/drwable/loading.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="<http://schemas.android.com/apk/res/android>">
    <item android:gravity= "center" android:width="100dp" android:height="100dp"> //바깥의 테두리 
        <rotate
            android:drawable="@drawable/loading_outer"
            android:pivotX="50%" 
            android:pivotY="50%"  
            android:fromDegrees="0"
            android:toDegrees="360"/>
    </item>
    <item android:drawable="@drawable/ic_android_black_24dp"//안쪽 이미지 
        android:gravity="center">
    </item>
</layer-list>
  • 안드로이드:피벗X : 회전축의 X좌표
  • 안드로이드:피벗Y : 회전축의 Y좌표(x,y)를 기준으로 !
  • android:fromDegrees : 시작할 회전 각도 (원본 이미지에서 이 각도만큼 시계 방향으로 회전하여 시작. ex a라는 이미지를 삽입하고 180으로 설정하면 b 모양에서 시작!
    )
  • 안드로이드: toDegrees : 끝 회전 각도
  • Android:너비 및 Android:높이 : 이미지 크기 조정
  • 진행률 표시줄에 만든 애니메이션을 적용하기만 하면 됩니다!

입술/레이아웃/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:app="<http://schemas.android.com/apk/res-auto>"
    xmlns:tools="<http://schemas.android.com/tools>"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progress_loading"
        style="@style/Widget.AppCompat.ProgressBar"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:indeterminateDrawable="@drawable/loading"
        android:indeterminateDuration="5000"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • android:indeterminateDrawable : 생성된 애니메이션을 여기에 적용합니다.

  • android: 무기한 지속 시간 : 생성된 애니메이션의 효과가 5000ms 동안 나타납니다.


다만 로딩 애니메이션을 항상 보여주지 않고 API 응답이 느릴 때만 로딩 UI를 보여주고 싶다면 ContentLoadingProgressBar사용할 수 있다고 합니다.

진행 애니메이션

: 이 애니메이션은 재생, 녹음, 다운로드와 같은 특정 작업의 진행 및 완료를 보여주고 싶을 때 사용됩니다.

이전 예제에서는 외부 이미지를 가져와서 회전시키는 방식으로 애니메이션을 만들었지만 여기서는 요소의 모양을 사용하여 진행률을 보여줍니다.

  1. 먼저 진행을 위한 배경을 만드세요!

res/drawable/progress_background.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="<http://schemas.android.com/apk/res/android>">
    <item> //외부 흰색 원 
        <shape android:shape="ring"
                android:thickness="5dp"
                android:useLevel="false">
            <solid android:color="@color/white"/>
        </shape>
    </item>
    <item android:drawable="@drawable/ic_android_black_24dp"//내부 이미지 
        android:width="100dp" android:height="100dp"
        android:gravity="center"/>
</layer-list>
  • 안드로이드:모양 : 사각형 | 타원형 | 라인 | 반지가 있다
  • 안드로이드: 두꺼운 : 진행 배경선의 두께를 설정합니다.

  • android:useLevel : 드로어블 부분적으로 드로어블하게 만들기!
    LevelListDrawable로 사용되는 경우 True, 일반적으로 False입니다.

    (true로 설정하면 보이지 않습니다.

    )추가 설명
  • 그런 다음 회전(이동)할 때 진행률을 표시하는 드로어블 파일을 만듭니다.

res/drawable/progress.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="<http://schemas.android.com/apk/res/android>"
    android:fromDegrees="270"
    android:toDegrees="270">
    <shape android:shape="ring"
        android:thickness="5dp"
        android:useLevel="true">
        <solid android:color="@color/purple_200"/>
    </shape>
</rotate>

코드는 이전 배경과 거의 동일하지만, useLevel = 참채우기 모양을 보려면 이렇게 해야 합니다.

(12:00부터 시작하려면 fromDegrees와 toDegrees를 각각 270으로 설정해야 합니다.

다른 각도에서 시작하려면 감을 잡으세요.)

  1. 마지막으로 이전 예제와 같이 진행률 표시줄에 적용합니다.

입술/레이아웃/activity_main.xml

<ProgressBar
        android:id="@+id/progress_charging"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:indeterminate="false"
        android:progressDrawable="@drawable/progress"
        android:background="@drawable/progress_background"
        android:max="500"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

https://developer.android.com/reference/android/widget/ProgressBar

  • 스타일 : 어떤 진행 상황이 발생했음을 나타내려면 스타일이 다음과 같아야 합니다.

    R.style.WidgetProgressBarHorizontal생성한 레이아웃을 적용하도록 설정해야 합니다.

  • 방해하다 : 이 모드는 진행률 표시줄의 진행률을 보여줍니다.

    무기한 모드는 진행률 표시줄의 기본 작동 모드이며 진행률을 표시할 때 명시적인 숫자 또는 범위 값을 사용하지 않습니다.

    작업이 진행 중임을 막연하게 나타냅니다.

    예) 서버와 통신할 때 답이 언제 도착할지 모른다면 (확실히) 결정 모드는 명시적인 개수 또는 값 범위를 제공하여 현재 진행 상황 표시예) 기온, 날씨앱 재생시간(https://recipes4dev.135)
  • → 이 예제에서는 활동의 진행 상황을 보기 위해 Progressbar.progress에 직접 값을 입력했기 때문에 interminate = false로 설정했습니다.

    (SetProgress는 불확정 모드에서 실행할 수 없습니다.

    )
  • 진행 드래그 가능 : 진행률을 보여주는 드로어블 파일 적용
  • 배경 : 생성된 배경 파일 적용

그리고 내가 만든 진행률 표시줄을 확인하고 진행률을 표시하는 스레드를 만들었습니다(데이터 바인딩과 뷰 바인딩은 생략했습니다!
).

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val progressBar = findViewById<ProgressBar>(R.id.progress_charging) 

        thread(start = true){
            var i = 1
            while(i<=500){
                i+=50
                runOnUiThread{
                    progressBar.progress = i
                }
                Thread.sleep(500)
            }
        }
    }
}


프레임 레이아웃

: 스플래시 화면으로도 잘 작동하는 애니메이션입니다.

설정된 시간 동안 서로의 이미지가 번갈아 표시됩니다.

  1. 지정된 기간 동안 여러 이미지를 표시하는 드로어블 파일을 만듭니다.

res/drawable/frame_loading.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="<http://schemas.android.com/apk/res/android>"
    android:oneshot="false">
    <item android:drawable="@drawable/ic1"
        android:duration="500"/>
    <item android:drawable="@drawable/ic2"
        android:duration="500"/>
    <item android:drawable="@drawable/ic3"
        android:duration="500"/>
</animation-list>
  • 안드로이드: 원샷 : 3개의 이미지를 연속으로 출력하며, 한 번만 출력할지, 여러 번 반복할지를 결정합니다 (https://j2enty.entry/Android-FrameAnimation)
  • Android: 기간 : 이미지 표시 시간 설정
  • 다음으로 위에서 만든 드로어블 파일을 AnimatedImageView에 적용하기만 하면 됩니다.

입술/레이아웃/activity_main.xml

<com.example.widget.AnimatedImageView
	android:src="http://mashup-android.m/@drawable/frame_loading/>

AnimatedStateListDrawable

: 상태에 따라 이미지나 탭 클릭 시 선택을 이용하여 이미지나 텍스트 색상을 변경했을 수 있습니다.

state_selected = true / false 이므로 위 예시에서 생성한 프레임 레이아웃을 2가지 상태로 변경할 수 있었고, 애니메이션 선택기를 사용하여 두 상태를 즉시 변경하지 않고 천천히 전환하는 효과를 만들 수 있습니다.

  1. 이전 예제에서 생성한 FrameLayout(각 프레임이 표시되는 순서대로)을 생성합니다.

    (좋아요 클릭시 하트가 애니메이션으로 변하면서 채워지는 이미지를 찍었습니다.

    )

res/drawable/charge_heart.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="<http://schemas.android.com/apk/res/android>"
    android:oneshot="true">
    <item android:drawable="@drawable/empty_heart"
        android:duration="100"/>
    <item android:drawable="@drawable/middle_heart"
        android:duration="100"/>
    <item android:drawable="@drawable/full_heart"
        android:duration="100"/>
    <item android:drawable="@drawable/last_heart"
        android:duration="100"/>
</animation-list>
  1. 전환 태그에서 만든 FrameLayout을 애니메이션 선택기에 적용합니다.

res/drawable/clicklikebtn.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="<http://schemas.android.com/apk/res/android>">
    <item android:id="@+id/selected"  
        android:drawable="@drawable/last_heart" //처음 클릭 했을때 이미지 
        android:state_selected="true" />
    <item android:id="@+id/unselected" //다시 클릭 했을때 이미지 
        android:drawable="@drawable/empty_heart"
        android:state_selected="false" />
    <transition
        android:drawable="@drawable/charge_heart"
        android:fromId="@id/unselected"
        android:toId="@id/selected"/>
</animated-selector>
  • android:fromId : 선택됨 → 선택되지 않음, 선택되지 않음 → 애니메이션을 시작할 상태를 선택함.
  • 안드로이드: toId : 애니메이션 종료 후 최종 상태를 결정합니다.

첫 번째 하트는 빈 마음으로 시작해서 가득 찬 마음으로 끝나기 때문에 선택하지 않은 이미지를 선택한 이미지 위에 올려놓습니다.

*좋아요를 다시 클릭할 때 애니메이션을 설정하려면 다른 전환을 만들고 Id=”selected”에서 Id=”unselected”로 전환하고 반전된 FrameLayout에 이미지를 배치할 수 있습니다 😉

  1. 애니메이션 선택기를 포함하는 드로어블 파일로 이미지 소스를 설정하고 클릭 시 isSelected를 변경하면 완료됩니다.

입술/레이아웃/activity_main.xml

<ImageView
        android:id="@+id/img_like"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="http://mashup-android.m/@drawable/click_like_btn"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

MainActivity.kt

val imgLike = findViewById<ImageView>(R.id.img_like)
imgLike.setOnClickListener {
     it.isSelected = true
}


프레임 레이아웃에서 프레임 수를 점차 늘리고 지속 시간을 줄이면 보다 자연스러운 애니메이션 효과를 얻을 수 있을 것 같습니다.

전환 애니메이션

마지막으로 화면 전환 시 가장 자주 나타나는 애니메이션을 적용해 보겠습니다.

예) 갤러리에서 이미지 선택 시 애니메이션

  • 공유 요소
  • : 액티비티나 프래그먼트를 전환할 때 각각의 경우에 지정된 컴포넌트가 분할된 것처럼 애니메이션 효과를 만드는 기법

머티리얼 디자인 기반이므로 API 레벨 21 이상이 필요합니다.

  1. 이미지를 표시할 ImageView를 만듭니다.

입술/레이아웃/activity_main.xml

<ImageView
        android:id="@+id/img_cat"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:transitionName="cat"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

원본 이미지를 확대하기 전에 볼 수 있는 레이아웃입니다.

입술/레이아웃/activity_detail.xml

<ImageView
        android:id="@+id/img_cat"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:transitionName="cat"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

확대 후 확대된 이미지를 볼 수 있는 레이아웃입니다.

여기서는 확대/축소 전 이미지를 표시하기 위해 imageView의 transitionName과 확대 후 이미지를 표시하기 위해 imageView의 transitionName에 동일한 값을 입력합니다.

  • Android: 전환 이름 : 애니메이션을 공유할 레이아웃에 사용되는 이름
  • 그런 다음 이미지를 설정하고 이동할 활동을 설정한 다음 URL을 전달합니다.

MainActivity.kt

val imgCat = findViewById<ImageView>(R.id.img_cat)
        val imgUrl = "https://lh3.googleusercontent.com/proxy/-Nf_Wz-eDqZjx_zspzQ1zs28W4XeEi-jDfTNthCFhipgJvjgRILpY-kWRFheRbveCJ4HrawAT3VsIoXZiPLFYwFR8oJxavAVGVfIlSrtk-29wViCQA"
        Glide.with(this).load(imgUrl).centerCrop().into(imgCat)

        imgCat.setOnClickListener {
            val intent = Intent(this, DetailActivity::class.java)
            intent.putExtra("resId",imgUrl)

            ActivityCompat.startActivity(
                this, intent,
                ActivityOptionsCompat
                    .makeSceneTransitionAnimation(
                        this, imgCat, imgCat.transitionName
                    ).toBundle()
            )
        }

글라이드 라이브러리를 이용하여 이미지 보기에서 이미지가 확대되도록 설정했습니다.

DetailActivity에 전달된 이미지는 이미지가 확장되는 애니메이션을 구현하므로 imgUrl 값을 putExtra에 전달하고 모션 애니메이션을 생성합니다.

ActivityOptionsCompat.makeSceneTransitionAnimation (컨텍스트, 이미지 보기, 전환 이름)을 입력합니다.

참조

  • ActivityOptionsCompat에서 makeScenceTransitionAnimation -makeScaleUpAnimation-makeThumbnailScaleUpAnimation 외에
  • . 나중에 공부하면 좋을 것 같습니다.

  • -makeClipRevealAnimation
  • -makeCustomAnimation
  • 확대된 사진을 보여주는 액티비티에 적용할 애니메이션 효과 설정

DetailActivity.kt

@RequiresApi(Build.VERSION_CODES.LOLLIPOP) //API 21이상부터 사용 
    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_detail)

        window.sharedElementEnterTransition =
            TransitionSet().apply{
                interpolator = OvershootInterpolator(0.5f)
                ordering = TransitionSet.ORDERING_TOGETHER
                addTransition(ChangeBounds().apply {
                    pathMotion = ArcMotion()
                })
                addTransition(ChangeTransform()) //scaltype 관련 
                addTransition(ChangeClipBounds())
                addTransition(ChangeImageTransform())
            }
        super.onCreate(savedInstanceState)

        val resId = intent.getStringExtra("resId")
        val imgCat = findViewById<ImageView>(R.id.img_cat)
        Glide.with(this).load(resId).into(imgCat)

        imgCat.setOnClickListener {
            finishAfterTransition()
        }
    }
  • window.sharedElementEnterTransition : 호출 활동의 종료 전환을 정의합니다.

  • 보간기 : 시작점에서 끝점까지 변화 과정을 표현하는 방법을 정의합니다.

    애니메이션이 완료된 후 약간의 바운스를 주기 위해 OvershootInterpolator를 사용합니다.

    (다른 보간기 효과에 유의하십시오.) (https://gus0000123.medium.com/android-animation-interpolar-to-implement-8d228f4fc3c3)
  • 아크모션() : 끊어지는 느낌 없이 부드러운 움직임을 위해 사용합니다.

  • finishAfterTransition() : 장면 전환 애니메이션을 반대로 합니다.

window.sharedElementEnterTransition { } 부분이 없어도 애니메이션 효과가 나타나긴 하지만, TransitionSet을 공부하면서 원하는 느낌을 적용해보면 좋을 것 같습니다.

🙂


언젠가 애니메이션을 써볼까 생각하다가 처음 접하게 되었어요 만드는 방법이 생소하지만 다양한 효과를 낼 수 있을 것 같아요. 다음에 recyclerView에서 애니메이션, ObjectorAnimator를 배워야 합니다.


참조

자원: https://developer.android.com/guide/topics/resources/drawable-resource?hl=ko

만화 : https://itmir.515

전환 애니메이션: https://developer.android.com/training/transitions/start-activity

공통 요소: https://mikescamell.com/shared-element-transitions-part-1/

이미지 출처: https://www.flaticon.com/packs/artificial-intelligence-31, http://www.foodnmed.com/news/articleView.html?idxno=18296