이렇게 플로팅 버튼을 이용해서 메뉴를 만드는 앱이 좀 많긴 하다.
글로 만든 메뉴보다 시각적이고 단순하며, 또 애니메이션과 함께라면 너무나도 귀여운(!) 앱을 만들 수 있다.
의외로 간단하니까 천천히 보자.
itstudy-mary.tistory.com/275?category=955763
여기서 이전 애니메이션에도 말한 적이 있는데, 단순히 ValueAnimator을 이용하면 레이아웃 뷰만 변화하고, xml로 뷰의 위치가 변하는건 아니기 때문에, 뷰의 변화가 없어도 되는 add 버튼은 valueAnimator로, 나머지는 ObjectAnimator로 구현한다.
먼저 버튼을 xml로 만든다.
- main_activity.xml
...
<ImageView
android:id="@+id/imageViewWrite"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_write_color"
android:layout_gravity="bottom|end"
android:background="@drawable/bg_round_button_white"
android:translationZ="5dp"
android:layout_marginEnd="25dp"
android:layout_marginBottom="25dp"
android:padding="15dp"
/>
<ImageView
android:id="@+id/imageViewPhoto"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_camera_fffff3"
android:layout_gravity="bottom|end"
android:background="@drawable/bg_round_button_a593e0"
android:translationZ="5dp"
android:layout_marginEnd="25dp"
android:layout_marginBottom="25dp"
android:padding="15dp"
/>
<ImageView
android:id="@+id/imageViewAdd"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_plus_fffff3"
android:layout_gravity="bottom|end"
android:background="@drawable/bg_round_button_566270"
android:translationZ="10dp"
android:layout_marginEnd="25dp"
android:layout_marginBottom="25dp"
android:padding="10dp"
/>
</FrameLayout>
(최상단 뷰를 frameLayout이나 relative, constantlayout으로 구현한다.)
이 때 add 버튼을 translationZ를 가장 높게, 나머지 두 뷰를 translationZ를 add 버튼보다는 낮게 구현한다.
다음, add버튼이 돌아가는 로테이션 애니메이션을 구현한다.
- rotate_plus_to_close.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:fromDegrees="0"
android:toDegrees="45"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fillAfter="true">
</rotate>
- rotate_close_to_plus.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:fromDegrees="45"
android:toDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fillAfter="true">
</rotate>
다음 코틀린 코드를 구현한다.
먼저, 전역변수가 필요하다.
var add: Boolean = true
왜냐면, 이 전역변수로 현재 메뉴가 열린 상태인지, 닫힌 상태인지 구분할 수 있도록 할 것이기 때문이다.
그리고, 메뉴를 구현할 뷰를 전역변수로 구현한다.
private lateinit var imageViewAdd: ImageView
private lateinit var imageViewWrite: ImageView
private lateinit var imageViewPhoto: ImageView
add버튼을 눌렀을때 메뉴가 나와야 하므로, add 버튼에 대한 리스너를 구현한다.
private fun setListener() {
...
imageViewAdd.setOnClickListener {
DlogUtil.d(TAG, "메뉴 클릭")
popupMenu()
}
}
버튼을 누를때 호출되는 팝업 메서드를 보자.
private fun popupMenu() {
var px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, this.resources.displayMetrics)
if (add) {
var animation = AnimationUtils.loadAnimation(this, R.anim.rotate_plus_to_close)
imageViewAdd.animation = animation
imageViewAdd.startAnimation(animation)
var writeAnimator = ObjectAnimator.ofFloat(imageViewWrite, "translationY", 0f, -px)
writeAnimator.duration = 400
writeAnimator.interpolator = OvershootInterpolator()
writeAnimator.target = imageViewWrite
writeAnimator.start()
var photoAnimator = ObjectAnimator.ofFloat(imageViewPhoto, "translationY", 0f, -px*2)
photoAnimator.duration = 500
photoAnimator.interpolator = OvershootInterpolator()
photoAnimator.target = imageViewPhoto
photoAnimator.start()
add = !add
} else {
var animation = AnimationUtils.loadAnimation(this, R.anim.rotate_close_to_plus)
imageViewAdd.animation = animation
imageViewAdd.startAnimation(animation)
var writeAnimator = ObjectAnimator.ofFloat(imageViewWrite, "translationY", -px, 0f)
writeAnimator.duration = 400
writeAnimator.interpolator = OvershootInterpolator()
writeAnimator.target = imageViewWrite
writeAnimator.start()
var photoAnimator = ObjectAnimator.ofFloat(imageViewPhoto, "translationY", -px*2, 0f)
photoAnimator.duration = 500
photoAnimator.interpolator = OvershootInterpolator()
photoAnimator.target = imageViewPhoto
photoAnimator.start()
add = !add
}
}
버튼이 true가 되면 value 애니메이션으로 버튼을 close로 45도로 돌리고,
var animation = AnimationUtils.loadAnimation(this, R.anim.rotate_plus_to_close)
imageViewAdd.animation = animation
imageViewAdd.startAnimation(animation)
write애니메이션과 photo 애니메이션을 Object 애니메이션으로 구현한다.
var writeAnimator = ObjectAnimator.ofFloat(imageViewWrite, "translationY", 0f, -px)
writeAnimator.duration = 400
writeAnimator.interpolator = OvershootInterpolator()
writeAnimator.target = imageViewWrite
writeAnimator.start()
var photoAnimator = ObjectAnimator.ofFloat(imageViewPhoto, "translationY", 0f, -px*2)
photoAnimator.duration = 500
photoAnimator.interpolator = OvershootInterpolator()
photoAnimator.target = imageViewPhoto
photoAnimator.start()
이 애니메이션은 위로 메뉴를 팝업하는 애니메이션이다.
이 때, 가장 위에 등장하는 메뉴가 가장 느려야 하며, 메뉴의 팝업속도가 다 같으면 굉장히 메뉴가 부자연스러워진다...
다음, 통통 튀는 애니메이션을 위해 interploator을 부여한다.
interploator 파라메터는 애니메이션의 진행 상태를 부여하는데, 기본은 linear 으로, 애니메이션이 모두 같은 속도로 움직인다.
지금 쓴 overShoot의 경우, 예상 진행경로보다 약간 더 움직이게 되고, Accelerator는 애니메이션의 시작 혹은 끝에 가속도를 부여한다.
writeAnimator.interpolator = OvershootInterpolator()
developer.android.com/reference/android/view/animation/Interpolator
다른 interpolator은 여기서 참고하자.
전체 코드는 다음과 같다.
- MainActivity.java
...
class MainActivity : AppCompatActivity() {
...
private lateinit var imageViewAdd: ImageView
private lateinit var imageViewWrite: ImageView
private lateinit var imageViewPhoto: ImageView
...
var add: Boolean = true
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
findView()
setListener()
...
}
...
private fun findView() {
...
imageViewAdd = findViewById(R.id.imageViewAdd)
imageViewWrite = findViewById(R.id.imageViewWrite)
imageViewPhoto = findViewById(R.id.imageViewPhoto)
...
}
private fun setListener() {
...
imageViewAdd.setOnClickListener {
DlogUtil.d(TAG, "메뉴 클릭")
popupMenu()
}
...
}
...
private fun popupMenu() {
var px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, this.resources.displayMetrics)
if (add) {
var animation = AnimationUtils.loadAnimation(this, R.anim.rotate_plus_to_close)
imageViewAdd.animation = animation
imageViewAdd.startAnimation(animation)
var writeAnimator = ObjectAnimator.ofFloat(imageViewWrite, "translationY", 0f, -px)
writeAnimator.duration = 400
writeAnimator.interpolator = OvershootInterpolator()
writeAnimator.target = imageViewWrite
writeAnimator.start()
var photoAnimator = ObjectAnimator.ofFloat(imageViewPhoto, "translationY", 0f, -px*2)
photoAnimator.duration = 400
photoAnimator.interpolator = OvershootInterpolator()
photoAnimator.target = imageViewPhoto
photoAnimator.start()
add = !add
} else {
var animation = AnimationUtils.loadAnimation(this, R.anim.rotate_close_to_plus)
imageViewAdd.animation = animation
imageViewAdd.startAnimation(animation)
var writeAnimator = ObjectAnimator.ofFloat(imageViewWrite, "translationY", -px, 0f)
writeAnimator.duration = 400
writeAnimator.interpolator = OvershootInterpolator()
writeAnimator.target = imageViewWrite
writeAnimator.start()
var photoAnimator = ObjectAnimator.ofFloat(imageViewPhoto, "translationY", -px*2, 0f)
photoAnimator.duration = 400
photoAnimator.interpolator = OvershootInterpolator()
photoAnimator.target = imageViewPhoto
photoAnimator.start()
add = !add
}
}
...
}
애니메이션을 이용하면 정말 섬세하게 앱 구현이 가능해진다.
'Project > 안드로이드 프로젝트(RandomColorChart)' 카테고리의 다른 글
Android Studio, Kotlin] 12. CameraX로 사진을 찍고 미리보기를 띄우기 (4) | 2021.03.15 |
---|---|
Android Studio, Kotiln] 11. 카메라 셔터st한 깜빡 애니메이션 구현 (2) | 2021.03.12 |
Android Studio, Kotiln] 8. 커스텀 토스트 만들기 (0) | 2021.02.18 |
Android Studio, Kotiln] 7. 지정 텍스트 복사 (0) | 2021.02.18 |
Android Studio, Kotlin] 6. (Open Source) Holo Color Picker (0) | 2021.02.17 |