[android 안드로이드] ontouchlistener 구현시 제스쳐 편하게 구현하기(클릭구현, 더블클릭구현 등등)
ontouchlistener 구현시 그냥 구현하게 되면 클릭부터 스크롤 심지어는 롱클릭 더블클릭등을 다 구현해야한다.
그래서 관련자료를 찾다가 발견하게된 것이 있어서 퍼왔다.
아래는 퍼온내용이다.
[Intro]
어플리케이션 개발을 하다보면 반드시 해야하는 것이 모션 이벤트 처리 입니다.
터치 이벤트 같은 것들은 DOWN - MOVE - UP의 단계를 거치면서
사용자가 어떤 동작을 입력 하는지 감지 할 수 있습니다.
이 입력의 어떤 조합으로 사용자가 어떤 동작을 했는지 감지 할 수 있겠죠.
하지만 직접 이런 제스쳐들을 구현하기란 쉬운 일만은 아닙니다. (무엇보다 귀찮죠~)
그래서 Android에서는 GestureDetector라는 클래스를 아얘 제공합니다.
[About GestureListener]
GestureDetector는 두 가지 Listener를 가지고 있습니다.
interface GestureDetector.OnDoubleTapListener
interface GestureDetector.OnGestureListener
http://developer.android.com/reference/android/view/GestureDetector.html
자세한 설명은 Reference를 보시면 됩니다.
OnDoubleTapListener는 이름 그대로 두번 터치 했을 때,
OnGestureListener는 일반적인 제스쳐들, 한번 터치나 스크롤 관련 Listner입니다.
그리고 저 두 가지 interface를 모두 가진 녀석이 있습니다.
class GestureDetector.SimpleOnGestureListener
보통 SimpleOnGestureListener만 extends 하면 모든 제스쳐를 다 사용 할 수 있습니다.
[Usage of GestureDetector]
사용법도 매우 간단합니다.
GestureDetector를 만들기만 하면 땡이죠.
mGestureDetector = new GestureDetector(this, new SimpleGestureListener());
mGestureDetector.onTouchEvent(event);
음... 너무 뜬금 없는 코드인가요? 일단 아주 간단하게 적어 봤습니다.
1. GestureDetector를 만들 때 GestureListener를 등록 하고
2. 감시할 MotionEvent를 onTouchEvent에 넣어 주면 GetstureListener가 호출이 되는 구조 입니다.
좀 더 자세하게 살펴 보면,
private final class SimpleGestureListener
extends GestureDetector.SimpleOnGestureListener {
// Implementation
}
private GestureDetector mGestureDetector;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mGestureDetector = new GestureDetector(this, new SimpleGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
위와 같습니다.
[Analyze Gestures]
사용법은 간단하지만 제스쳐는 그리 간단하지 않습니다.
그래서 각 제스쳐에 대해서 분석을 해봤습니다.
public boolean onDoubleTap(MotionEvent e)
public boolean onDoubleTapEvent(MotionEvent e)
public boolean onDown(MotionEvent e)
public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY)
public void onLongPress(MotionEvent e)
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY)
public void onShowPress(MotionEvent e)
public boolean onSingleTapConfirmed(MotionEvent e)
public boolean onSingleTapUp(MotionEvent e)
일단 제스쳐 이벤트에 대해서 간단히 살펴 봅시다.
onDoubleTap은 두 번 터치입니다.
onDoubleTap은 두 번 터치하면 이벤트가 더 이상 발생 되지 않지만,
onDoubleTapEvent는 DOWN, MOVE, UP 이벤트를 모두 포함 합니다. (나중에 살펴 보겠습니다.)
onDown은 터치하려고 손을 대기만 해도 발생되는 이벤트며,
모든 제스쳐의 시작입니다.
onFling은 onScroll에서 끝을 살짝 튕기는 동작에서 발생하는 이벤트며,
onScroll은 말 그대로 스크롤 시에 발생하는 이벤트 입니다.
onLongPress는 길게 눌렀을 때 발생 하는 이벤트며,
onShowPress는 onLongPress보다는 좀 더 짧은 시간동안 누르고 있으면 발생 하는 이벤트 입니다.
onSingleTap은 한 번 터치 했을 때 발생하는 이벤트며,
onSingleTabConfirmed는 한 번 터치 하고 다음에 다시 터치 이벤트가 들어오지 않았을 때,
한 번 터치가 확실 하다고 확인 시켜주는 이벤트 입니다.
자... 이제 시작입니다! 이건 간단히 살펴 본것 밖에 안되는...
본격적으로 실제 제스쳐에 따라서 어떤 이벤트가 호출 되는지 살펴 보겠습니다.
시간은 로그를 통해 한번만 계산된 시간이며,
대략 그 시간 범위에서 이벤트가 발생 한다고 보시면 됩니다.
1. 아주 살짝 터치
000ms onDown : ACTION_DOWN
060ms onSingleTapUp : ACTION_UP
306ms onSingleTapConfirmed : ACTION_DOWN
일단 손을 대면 무조건 onDown 이벤트 발생입니다.
살짝 터치를 하게 되면 보통 30~60ms 정도 후에 손이 떨어지게 됩니다.
손이 떨어 지면 onSingleTapUp 이벤트가 발생하며,
onDown이벤트 발생 후 약 300ms 안에 다시 onDown 이벤트가 발생 하지 않는다면
onSingleTapConfirmed 이벤트가 발생하여 확실히 한 번 터치 되었다는 이벤트를 발생 시킵니다.
2. 살짝 터치
000ms onDown : ACTION_DOWN
097ms onShowPress : ACTION_DOWN
172ms onSingleTapUp : ACTION_UP
303ms onSingleTapConfirmed : ACTION_DOWN
역시나 onDown 이벤트 부터 시작입니다.
1번 보다는 살짝 길게, 약 90~100ms 정도 터치되면 onShowPress 이벤트가 발생합니다.
172ms 이후에 손을 떼었으며,
역시나 300ms 정도 지나면 onSingleTapConfirmed 이벤트가 발생 됩니다.
3. 약간 길게 터치
000ms onDown : ACTION_DOWN
103ms onShowPress : ACTION_DOWN
460ms onSingleTapUp : ACTION_UP
2번보다 좀 더 길지만, LongPress는 아닌 상황입니다.
역시 이 때도 약 100ms 정도에 onShowPress 이벤트가 발생하긴 하지만
300ms 이후에 손을 떼었기 때문에 onSingleTapConfirmed 이벤트가 먹히는 현상이 일어납니다.
4. 아주 길게 터치
000ms onDown : ACTION_DOWN
096ms onShowPress : ACTION_DOWN
590ms onLongPress : ACTION_DOWN
LongPress가 발생 하는 상황입니다.
100ms 정도에 onShowPress 이벤트가 발생 하며,
약 590~600ms 정도에 onLongPress 이벤트가 발생 합니다.
이때 onSingleTapUp 이벤트는 발생 하지 않습니다.
두 번 터치 하는 경우에는 이벤트가 두 가지이기 때문에,
두 가지 조합의 경우 모두 살펴 보겠습니다.
5. 두 번 터치 (onDoubleTap)
000ms onDown : ACTION_DOWN
060ms onSingleTapUp : ACTION_UP
140ms onDoubleTap : ACTION_DOWN
140ms onDown : ACTION_DOWN (지연 될 수 있음)
먼저 onSingleTapUp 이벤트가 발생 합니다.
그리고 onSingleTapConfirmed 가 발생 하기 전에
다시 onDown 이벤트가 들어오게 되면 onDoubleTap 이벤트가 발생 합니다.
참고로 두번째 들어오는 onDown 이벤트는 onDoubleTap 이벤트보다 늦게 들어 올 수 있습니다.
(항상 같이 들어 오는게 아니라 onDoubleTap이 먼저 발생 합니다.)
6. 두 번 터치 (onDoubleTapEvent)
000ms onDown : ACTION_DOWN
041ms onSingleTapUp : ACTION_UP
130ms onDoubleTapEvent : ACTION_DOWN
130ms onDown : ACTION_DOWN (지연 될 수 있음)
190ms onDoubleTapEvent : ACTION_UP
onDoubleTap 이벤트와의 차이는 DOWN, MOVE, UP 이벤트까지 모두 캐치된다는 점이며,
마지막에 onDoubleTapEvent에 UP 액션이 들어오는 것을 확인 할 수 있습니다.
(위의 경우, 190ms 이후에 두번째 터치에서 손이 떨어졌다는 것을 확인 할 수 있습니다.)
7. 두 번 터치 (onDoubleTap + onDoubleTapEvent)
000ms onDown : ACTION_DOWN
080ms onSingleTapUp : ACTION_UP
200ms onDoubleTap : ACTION_DOWN
200ms onDoubleTapEvent : ACTION_DOWN (지연 될 수 있음)
200ms onDown : ACTION_DOWN (지연 될 수 있음)
260ms onDoubleTapEvent : ACTION_UP
같이 사용 하게 되면 onDoubleTap, onDoubleTapEvent 이벤트 둘 다 발생하며,
항상 onDoubleTap 이벤트가 먼저 발생 하게 됩니다.
(결과적으로 onDoubleTap - onDoubleTapEvent - onDown 순서로 발생 합니다.)
8. 두 번 터치, 두 번째 터치시 스크롤 (onDoubleTapEvent)
000ms onDown : ACTION_DOWN
080ms onSingleTapUp : ACTION_UP
179ms onDoubleTapEvent : ACTION_DOWN
179ms onDown : ACTION_DOWN (지연 될 수 있음)
280ms onDoubleTapEvent : ACTION_MOVE
289ms onShowPress : ACTION_DOWN290ms onDoubleTapEvent : ACTION_MOVE
...
779ms onLongPress : ACTION_DOWN
800ms onDoubleTapEvent : ACTION_UP
평소에 절대 나올법한 제스쳐지만 onDoubleTapEvent의 특성을 살펴보기 위한 제스쳐 입니다.
두 번째 onDown 이벤트 이후에 MOVE 이벤트가 들어 오는 것을 확인 할 수 있으며,
한가지 특이한 점은 계속 스크롤 되지 않고 onLongPress 이벤트가 발생하면 끝난다는 점입니다.
손을 뗄 수 밖에 없는 상황이 오게 되죠.
위에서 보시다 시피,
조금 길게 눌러짐에 따라 onShowPress와 onLongPress가 도중에 발생 할 수도 있습니다.
9. 스크롤
000ms onDown : ACTION_DOWN
030ms onScroll : ACTION_DOWN, ACTION_MOVE
...
스크롤 이벤트는 간단 합니다.
최소 30ms 이후 부터는 onScroll 이벤트가 발생 할 수 있으며,
플링시키지 않고 살며시 손을 떼면 끝까지 onScroll 이벤트만 연속으로 발생 합니다.
10. 플링
000ms onDown : ACTION_DOWN
030ms onScroll : ACTION_DOWN, ACTION_MOVE
...
900ms onFling : ACTION_DOWN, ACTION_UP
마지막에 손가락을 슬며시 튕기는 플링 동작입니다.
스크롤 이벤트와 비슷하지만, 마지막에 UP 액션과 함께 onFling 이벤트가 동작합니다.
스크롤과 플링 제스쳐 모두 시간에 따라 onShowPress 이벤트가 발생 할 수 있습니다.
[Outro]
Android에 기본으로(API Level 1) 들어 있는 GestureDetector에 대해서
조금은 자세하게 알아 봤습니다.
그냥 막연하게 이벤트 이름만 보고서 프로그래밍을 하기 보다는,
정확하게 어떻게 동작이 되는지 확인 하고 프로그래밍을 하면
좀 더 자신이 원하는 제스쳐를 캐치하여 좀 더 나이스한 어플리케이션을 만들 수 있을 것입니다.
출처 : http://blog.naver.com/PostView.nhn?blogId=visualc98&logNo=94697746&viewDate=¤tPage=1&listtype=0&userTopListOpen=false&userTopListCount=5&userTopListManageOpen=false&userTopListCurrentPage=undefined
아래는 내가 테스트해보기 위해 만든 간단한 코드이다. onCreate 부분
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
View v= findViewById(R.id.mapview);
final GestureDetector g = new GestureDetector(new OnGestureListener() {
public boolean onSingleTapUp(MotionEvent e) {
Log.w("shh", "onSingleTapUp");
return true;
}
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
Log.w("shh", "onShowPress");
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
Log.w("shh", "onScroll");
return true;
}
public void onLongPress(MotionEvent e) {
Log.w("shh", "onLongPress");
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.w("shh", "onFling");
return true;
}
public boolean onDown(MotionEvent e) {
Log.w("shh", "onDown");
return true;
}
});
v.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return g.onTouchEvent(event);
}
});
}