안드로이드 개발에 있어서 앱에 더욱 활기를 불어넣어 줄 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에 존재를 배워서 무사히 예제를 구현할 수 있었다. 코틀린을 사용하는 게 아직은 익숙지가 않기 때문에 좀 더 공부가 필요한 것 같다.
'안드로이드' 카테고리의 다른 글
[Android/Kotiln] DataBinding(데이터바인딩)3 - Observable(실시간 데이터) (1) | 2020.02.06 |
---|---|
[Android/Kotlin] DataBinding(데이터바인딩) 2 - BindingAdapter(바인딩 어댑터) (0) | 2020.01.31 |
[Android/Kotlin] DataBinding(데이터바인딩) 1 - 기초 (2) | 2020.01.24 |
자 이제 무엇을 할것이냐? (MVVM 패턴) (0) | 2020.01.15 |
[Kotiln/Android] TabLayout과 ViewPager를 이용한 화면 이동 (3) | 2019.12.18 |