24.01.17 MotionLayout으로 간단한 애니메이션 화면 만들기

KSang·2024년 1월 22일
0

TIL

목록 보기
34/101

MotionLayout

모션 레이아웃을 이용하면

앱을 구현할때 애니메이션 효과를 주는 등 다양하게 표현이 가능하다

2024-01-2221-25-07-ezgif com-crop

이번에 만든 인트로 화면인데

스와이프하면서 통화하는걸 표현해봤다.

스와이프, 클릭등 액션 없이 그냥 재생만 되는 애니메이션이라 간단 할 줄 알았는데

어떻게 만드는지 자료를 찾기가 좀 힘들었다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/intro_motion"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    app:layoutDescription="@xml/activity_intro_scene">

    <ImageView
        android:id="@+id/iv_intro_skyline"
        android:layout_width="match_parent"
        
        ...

우선 레이아웃을 만들때 모션레이아웃으로 전체를 감싸준다.

모션 레이아웃은 constraint Layout을 상속받기 때문에 그와 유사하게

뷰의 위치를 잡아주면 된다.

그렇게 만들어주면 xml에 scene파일이 하나 생기게 되는데 여기서 애니메이션을 설정해줄 수 있다.

intro_scene

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:motion="http://schemas.android.com/tools">

    <ConstraintSet android:id="@+id/start">
        <!-- Constraints for other views -->
        <Constraint
            android:id="@+id/tv_intro_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:alpha="0"
            android:fontFamily="@font/shrikhand_regular"
            android:text="Color\n Contatcs"
            android:textColor="@color/white"
            android:textSize="56sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.2"/>

            ...

    <ConstraintSet android:id="@+id/End">
        <!-- Constraints for other views -->
        <Constraint
            android:id="@+id/tv_intro_title"
            android:layout_width="wrap_content"
            ...

일단 트랜지션을 설정해줘야한다

start는 애니메이션 시작할때 뷰들의 위치와 상태를 정의해주고

end는 애니메이션이 끝날때 뷰들의 위치와 상태를 나타낸다.

image

모션레이아웃으로 돌아가 디자인 탭으로 보면 이런식으로 트랜지션이 생긴걸 볼 수 있다.

여기서 트랜지션을 눌러보면 아래에 체크할 수 있는 부분이있는데,

여기에서 scene따로 작성하지 않고도 모션 레이아웃의 뷰를 트랜지션에 추가 할 수 있다.

여기서 애니메이션의 효과를 주려면

<Transition
        app:constraintSetEnd="@id/end"
        app:constraintSetStart="@id/start"
        motion:duration="4000">
        <KeyFrameSet>
            <KeyAttribute
                android:alpha="0"
                app:framePosition="10"
                app:motionTarget="@id/iv_intro_skyline"
                app:transitionEasing="accelerate" />
            <KeyPosition
                motion:keyPositionType="parentRelative"
                app:percentX="1"
                motion:framePosition="20"
                app:motionTarget ="@+id/iv_intro_skyline"
                app:keyPositionType="pathRelative"
                app:framePosition="30" />

                ...

scence에선 이렇게 지정 할 수 가 있다.

Transition에서 시작 트랜지션과 끝 트랜지션을 적어주고

애니메이션이 몇동안 지속될지를 작성한다

그런다음 KeyAttribute로 뷰의 상태, 여기선 투명도를 정해줬고 그안에 app:framePosition="10"적어서 애니메이션의 10% 즉 0.4초까지 투명도가 0이되게 설정했다

시작시 투명도가 0이 아닌경우 0.4초동안 투명해지는 효과가 있고 투명도가 0인경우는 0.4초까지 계속해서 투명하게 나타낼 수 있다

KeyPosition으론 View의 위치를 나타내는데 여기서 위치가 어떻게 변할지를 정할 수 있다.

간단하겐 직선으로 쭉 이동할 수도 있지만 좌우로 휘며 이동하는 등 효과도 낼 수 있다.

이런 작업을 View하나하나에 전부 해줘야하는데 치다보니 좀 속이 울렁거리는 것 같았다

그런데 알고보니 모션레이아웃의 디자인탭에서도 수정이 가능했다

image

여기서 타이머를 눌러서 다양한 키프레임을 설정 할 수 있었다.

레이아웃도 그냥 마크업으로 작성했었는데

모션레이아웃 작업은 확실히 디자인탭을 이용하는게 훨씬 빠르고 편한것 같다

여기서 애니메이션을 중간중간 재생하며 확인 할 수도있고 상당히 편하게 작업 할 수 있었다.

이런식으로 작업을 하면 아까 위에서 보여준 짧은 애니메이션을 만들 수 있다.

2024-01-2221-25-07-ezgif com-crop

이제 이걸 실제로 쓰려면 코드를 짜야하는데 모션레이아웃은 액티비티에서 어떻게 짤까?

IntroActivity.kt

    private fun startMotion() {
        binding.introMotion.apply {
            transitionToStart()
            binding.tvIntroTouch.startAnimation(blick)
            Handler(Looper.getMainLooper()).postDelayed({
                goMain()
            }, 4000)
        }
    }

    private fun goMain() {
        binding.introMotion.transitionToEnd()

        binding.introMotion.setOnClickListener {
            startActivity(Intent(this@IntroActivity, MainActivity::class.java))
            finish()
        }
    }

처음엔 이렇게 짰었다.

binding.introMotion.transitionToEnd()가 나오면 엔딩 까지 바로 점프되면서 애니메이션이 순식간에 지나가기 때문에 뭔가 시작이라는 느낌의 transitionToStart()를 넣고 딜레이를 4000을 줘서 애니 메이션이 실행 될 수 있지 않을까? 생각했는데

그냥 4초동안 가만히 있다가 End로 점프할 뿐이었다.

한참 고민하다가 binding.introMotion.Tran 까지 치고 자동완성되는 메소드들을 조사하기 시작했다.

그러다가 binding.introMotion.set 을 조사하기 시작했는데

setTransitionDuration이라는 딱 봐도 중요할것 같은 느낌의 메소드가 있었다

찾아보니 end가 되기전에 duration을 설정하면 그 시간동안 애니메이션이 실행되게 하는 것 이었다.

    private fun goMain() {
        binding.introMotion.setTransitionDuration(4000)
        binding.introMotion.transitionToEnd()

        binding.introMotion.setOnClickListener {
            startActivity(Intent(this@IntroActivity, MainActivity::class.java))
            finish()
        }
    }

최종적으로 이런식으로 수정해서 완성할 수 있었다.

2024-01-2221-58-14-ezgif com-video-to-gif-converter

0개의 댓글