안드로이드

AnimationListener를 이용한 페이지 슬라이드

mingmaeng 2019. 12. 13. 16:52

AnimationListener를 이용한 PageSlide

안드로이드 개발에 있어서 앱에 더욱 활기를 불어넣어 줄 Animation에 대해 보자. Animation에는 scale, translate, rotate, alpha 등 다양한 애니메이션 효과들이 존재하고 이를 적절히 섞어 내가 원하는 애니메이션을 만들 수 있다.

이번 포스팅에서는 햄버거 메뉴바처럼 버튼을 누르면 옆에서 또 하나의 페이지가 나오는 예제를 포스팅하겠다.

translate_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/accelerate_decelerate_interpolator">

    <translate
        android:fromXDelta="100%p"
        android:toXDelta="0%p"
        android:duration = "500"
        android:repeatCount ="0"
        android:fillAfter="true"/>

</set>

 

translate_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/accelerate_decelerate_interpolator">

    <translate
        android:fromXDelta="0%p"
        android:toXDelta="100%p"
        android:duration = "500"
        android:repeatCount ="0"
        android:fillAfter="true"/>

</set>

 

res 폴더에 anim 폴더를 만들고 애니메이션을 설정하는 xml을 만든다. left와 right의 차이점은 fromXDelta와 toXDelta의 차이점이다.

코드에 대한 기능을 살짝 보면 다음과 같다.

  • fromXDelta : x좌표에 대한 시작 위치
  • toXDelta : x좌표에 대한 최종 이동 위치
  • fillAfter : true로 설정할 경우 원래 자리로 돌아가지 않는다.

해당 xml은 translate에 대한 애니메이션을 만들어 놓은 것이며, 버튼을 클릭했을 때 화면에 나오고 들어가는 애니메이션을 표현한 것이다.

 

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#ff5555ff">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Base Area"
            android:textColor="#ffffffff"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/page"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_gravity="right"
        android:background="#ffffff66"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Area #1"
            android:textColor="#ff000000"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Area #2"
            android:textColor="#ff000000"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="right|center_vertical"
        android:background="#00000000">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Open"
            />
    </LinearLayout>

</FrameLayout>

메인화면에 보여질 xml이다. 가운데 있는 LinearLayout이 애니메이션으로 보일 페이지이며 마지막 LinearLayout은 버튼을 놓아둔 Layout이다.

 

MainActivity.java
package org.techtown.samplepagesliding_java;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    boolean isPageOpen = false;
    Animation LeftAnim;
    Animation RightAnim;

    LinearLayout page;
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        page = findViewById(R.id.page);

        LeftAnim = AnimationUtils.loadAnimation(this, R.anim.translate_left);
        RightAnim = AnimationUtils.loadAnimation(this, R.anim.translate_right);

        SlidingPageAnimationListener animListener = new SlidingPageAnimationListener();
        LeftAnim.setAnimationListener(animListener);
        RightAnim.setAnimationListener(animListener);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isPageOpen){
                    page.startAnimation(RightAnim);
                }
                else{
                    page.setVisibility(View.VISIBLE);
                    page.startAnimation(LeftAnim);
                }
            }
        });
    }

    private class SlidingPageAnimationListener implements Animation.AnimationListener{
        public void onAnimationEnd(Animation animation){
            if(isPageOpen){
                page.setVisibility(View.INVISIBLE);

                button.setText("Open");
                isPageOpen = false;
            }
            else{
                button.setText("Close");
                isPageOpen = true;
            }
        }
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    }
}

메인 코드다. LeftAnim 과 RightAnim에 각각 translate_left.xml과 translate_right.xml이 들어있고, AnimationListener를 상속받은 SlidingPageAnimationListener를 연결해줬다. SlidingPageAnimationListener에서 애니메이션 상태에 따른 변화를 담당한다. 3가지 함수가 반드시 선언돼야 하는데 다음과 같다.

 

  • onAnimationStart : 애니메이션이 시작되기 전 호출
  • onAnimationEnd :애니메이션이 끝났을 때 호출
  • onAnimationRepeat : 애니메이션이 반복될 때 호출
MainActivity.kt
package org.techtown.samplepagesliding


import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

    var isPageOpen : Boolean = false
    lateinit var LeftAnim : Animation
    lateinit var RightAnim : Animation

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

        LeftAnim = AnimationUtils.loadAnimation(this, R.anim.translate_left)
        RightAnim = AnimationUtils.loadAnimation(this,R.anim.translate_right)

        LeftAnim.setAnimationListener(SlidingPageAnimationListener())
        RightAnim.setAnimationListener(SlidingPageAnimationListener())

        button.setOnClickListener {
            if (isPageOpen) {
                page.startAnimation(RightAnim)
            }
            else{
                page.visibility = View.VISIBLE
                page.startAnimation(LeftAnim)
            }
        }
    }

    private inner class SlidingPageAnimationListener : Animation.AnimationListener {
        
        override fun onAnimationEnd(animation: Animation?) : Unit {
            if(isPageOpen){
                page.visibility=View.INVISIBLE

                button.text="Open"
                isPageOpen = false
            }
            else{
                button.text="Close"
                isPageOpen = true
            }
        }

        override fun onAnimationStart(animation: Animation?) {

        }

        override fun onAnimationRepeat(animation: Animation?) {
        }
    }
}

코틀린으로 구현할 경우는 다음과 같다.

다른 점이 있다면 코틀린으로는 익스텐션을 이용해서 xml에서 지정해 놓은 id값에 직접 접근했다는 점과, SlidingPageAnimationListener 클래스 안에서 변수를 써야 하기 때문에 inner class로 구현하여 외부 class인 MainActivity에 존재하는 변수와 익스텐션을 사용했다.

 


이번 예제를 하면서 코틀린을 사용할 경우 SlidingPageAnimationListener 안에서 isPageOpen 변수를 사용할 수가 없어서 엄청 헤매다가 inner class에 존재를 배워서 무사히 예제를 구현할 수 있었다. 코틀린을 사용하는 게 아직은 익숙지가 않기 때문에 좀 더 공부가 필요한 것 같다.