핸드폰의 화면이 넓어짐에 따라, 이전 화면 이동을 위해 back 키를 누르는 것이 불편해지고 있습니다.
이를 위해 아이폰의 스와이프하여 뒤로가기 기능을 유사하게 만들어보겠습니다.
위의 영상과 같이 왼쪽에서 오른쪽으로 스와이프를 하면 화면이 닫히는(이전화면으로 이동하는) 기능입니다.
위의 기능은 GestureDetector에 TouchEvent를 전달하여 사용자 (왼쪽->오른쪽 이동)제스쳐를 감지하여 화면을 종료하도록 구현할 예정입니다.
1. GestureListener를 왼쪽 -> 오른쪽으로 이동하는 제스춰를 감지하도록 구현합니다.
interface OnSwipeCallback {
fun onLeft()
fun onRight()
}
class OnSwipeGestureListener(val callback: OnSwipeCallback) :
GestureDetector.SimpleOnGestureListener() {
companion object {
private const val SWIPE_THRESHOLD = 350
private const val SWIPE_VELOCITY_THRESHOLD = 350
}
override fun onDown(e: MotionEvent?): Boolean = false
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
try {
if (e2 == null || e1 == null) return false
val diffX = e2.x - e1.x
if (abs(diffX) > SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
callback.onRight()
} else {
callback.onLeft()
}
return true
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
}
사용자가 스와이프를 하여 터치가 up이 되면 onFling() 이벤트가 전달됩니다.
그러며, x좌표(가로방향)의 이동 거리와 이동 속도를 체크하여 callback을 호출하게 됩니다.
(코드상으로는 이동 거리와 속도를 각각 350으로 설정하였습니다. - 그냥 적당해 보였습니다.ㅎㅎ)
2. TouchListener의 이벤트를 GestureDetector에 연결해 줍니다.
class OnSwipeTouchListener(context: Context, callback: OnSwipeCallback) : View.OnTouchListener {
private val gestureDetector = GestureDetector(context, OnSwipeGestureListener(callback))
fun getGestureDetector() = gestureDetector
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(event)
}
}
파라메터로 받은 OnSwipeCallback을 GestureDetector에 전달하여 제스춰 감지시에 Activity 쪽으로 이벤트를 전달할 수 있도록 합니다.
getGestureDetector() 함수는 OnTouchListener를 사용하지 않는 View에서 GestureDetector를 사용하기 위함입니다.
3. View와 연동을 구현할 차례입니다.
fun Activity.setOnSwipeListener(vararg views: View) {
val onSwipeTouchListener =
OnSwipeTouchListener(this, object : OnSwipeCallback {
override fun onLeft() {}
override fun onRight() {
finish()
overridePendingTransition(R.anim.anim_enter_no_change, R.anim.anim_exit_slide_right)
}
})
views.forEach { view ->
if (view is RecyclerView) {
view.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean =
onSwipeTouchListener.getGestureDetector().onTouchEvent(e)
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})
} else {
view.setOnTouchListener(onSwipeTouchListener)
}
}
window.decorView.rootView.setOnTouchListener(onSwipeTouchListener)
}
모든 Activity에서 사용할 수 있도록 extension으로 구현합니다.
OnSwipeTouchListener를 생성하면서 OnSwipeCallback을 구현하고, onRight() 함수에 화면 종료 코드를 추가합니다.
그리고, Activity 종료시에 왼쪽->오른쪽으로 이동하고 사라지도록 애니메이션도 추가해 줍니다.
파라메터라 받은 View에 TouchListner를 연동해줍니다.
파라메터가 vararg 로 array 대응으로 forEach를 사용합니다.
RecyclerView는 Item에 TouchListener에 등록해야 하기에 별도로 처리해 줍니다.
4. Activity에 SwipeListener를 추가해줍니다.
class SwipeFinishExampleActivity :
BaseActivity<ActivitySwipeFinishExampleBinding>(R.layout.activity_swipe_finish_example) {
override fun initView() {
super.initView()
setOnSwipeListener()
}
}
setOnSwipeListener() 함수의 파라메터로 layout에서 사용하는 View를 연동시켜주면 됩니다.
(위의 코드에서는 특별히 View가 없어서 추가하지 않았습니다)
전체 코드는 아래 GIthub 링크를 확인해 주세요.
GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.
https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함. - GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.
github.com
'Android + Kotlin' 카테고리의 다른 글
[Android Kotlin] sealed class로 Network Response (with Flow)를 구현해보자. (0) | 2022.03.16 |
---|---|
adb: more than one device/emulator 문제 해결하기 (0) | 2022.03.14 |
[Android Kotlin] @BindingAdapter 를 사용하여 DataBinding 끝내기 (0) | 2022.02.09 |
[Android Kotlin] TransactionTooLargeException 해결 방법 (0) | 2022.01.25 |
[Android Kotlin] 안전하게 Enum 타입 사용하기 (0) | 2022.01.24 |