Tistory View

Android Develop/Input

안드로이드 Touch다루기 : 기본 제스처

God Dangchy What should I do? 2019. 12. 26. 08:56

이전편에서 설명한 Touch만으로는 단순한 작업만이 가능하다. 터치를 추적해서 이동을 한다거나, 클릭정도만 처리가 가능하기 때문에, 페이지를 넘긴다든지, 천천히 스크롤을 한다든지하는 사용자의 편의성을 위해 제공하는 제스처들을 바로 처리할 수 있게 만들어 두었다. 실제, 이 것을 일일이 만든다는 것은 쉬운일이 아니다. 특히 손가락을 튕기는 fling의 경우는 정말 만들기 어렵다. 터치점들을 일일이 추적하여 산수공식에 넣고, 기기마다 일일이 테스트를 거쳐야 하기 때문에, 수많은 시행착오를 거치다 못해 포기하는 경우가 다반사다. 구글은 이 작업을 쉽게 할 수 있게 미리 만들어 두었다. (땡큐 구글~) 이번 글에서는 click, fling, scroll, longpress등을 다루게 될 것이다.

 

이미지 하나로 울거먹기~

 

기본적으로 지원하는 제스쳐는 다음과 같다.

 

GestureDetector.OnGestureListener interface

동작 내용
onDown 터치하는 순간
onShowPress 터치하는 순간 터치가 되었음을 사용자에게 알리기 위한 작업을 처리하는 용으로 버튼의 색을 바꾼다던지, 목록의 항목일 경우 배경색을 바꾼다던지하는 작업을 위한 것이다.
onSingleTapUp 클릭되었을 때의 작업처리
onScroll 사용자가 화면에 손을 대고 움직일 때 작업처리
onLongPress 사용자가 터치 후 대기 중일때
onFling 사용자가 손가락을 튕겼을 때

위의 동작은 기본적인 동작이지만, 특별한 상황을 제외하고는 이 것을 구현하는 것만으로도 충분한 경우가 대부분이다.

 

예제 : 주석에 설명을 달아 두었다.

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class GestureDectectActivity extends AppCompatActivity
implements View.OnTouchListener
         // activity에서 처리를 할 것이므로 implement한다.
        , GestureDetector.OnGestureListener
{

    private static final String TAG = "GestureDectectActivity";

    GestureDetector mDetector;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gesture_dectect);

        View v = this.findViewById( R.id.vw_touch );

        // Detector를 만들고 onDown등의 이벤트처리를 위해 현재 Activity를 지정
        // 첫번째 param은 Context이고 두번째가 이벤트핸들러다
        mDetector = new GestureDetector( this, this );
        
        // Detector를 만들었을 뿐, 실제 Detector에게 touchEvent를 보내주기 위해
        // OnTouchListener를 지정하여 onTouchEvent에서 mDetector에 전달한다.
        v.setOnTouchListener( this );
        
        /*
        예제라서 Activity를 사용했을 뿐 실제 여러뷰를 처리하려면, 뷰마다 디텍터를 생성하고
        다음과 같이 핸들어를 만들어야 할 것이다.
        mDetector = new GestureDetector( this, new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }
            .
            .
            .
            .
        });
        
        뷰.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
            	mDetector.onTouchEvent( event );
                return true;
            }
        });
        */
               
        
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        
        // 터치이벤트를 전부 Detector에게 넘김다.
        // 이제 사용자의 손가락 동작에 따라
        // onSingleTapUp, ofling, onScroll 등이 호출될 것이다.
        mDetector.onTouchEvent( event );
        
        return true; // 난 모든 이벤트가 필요하다 // 귀찮으니..
    }
    
    @Override
    public boolean onDown(MotionEvent e) {
        Log.d( TAG, "onDown" );
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e)
    {
        Log.d( TAG, "onShowPress" );
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.d( TAG, "onSingleTapUp" );
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        Log.d( TAG, "onScroll" );
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        Log.d( TAG, "onLongPress" );
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        Log.d( TAG, "onFling" );
        return false;
    }
}

예제는 Activity를 사용했지만 실제는 View마다 다른 Detector를 사욯하는 것이다. 좋을 것 같다. 필자가 onDown등의 핸들어에서 어떤 View에 종속된 것인지 구분하는 방법을 찾긴 했지만 좀 문제있어 보였다.(좋은 방법 아시는 분 댓글 좀..)

 

 

SimpleOnGestureListener

GestureDetector.OnGestureListener는 위에서 다루어 보았다. 실제 프로그램을 만들 때는 위의 interface내의 모든 함수를 전부처리할 경우는 그리 많지 않다, OnGestureListener는 interface기 때문에, 사용하지 않는 것도 일단 만들어는 놔야한다. 그러다 보면 코드가 길어지고 알아보기 힘들기 때문에, SimpleOnGestureListener를 구글에서 만들어 놨다.

OnGestureListener는 Interface지만 SimpleOnGestureListener는 class로 만들어져 있다. 따라서 필요한 부분만 Override해서 사용하면 된다. 문제는 Java는 다중상속이 되지 않는다는 문제인데, 상속보다는 그냥 변수로 바로 만들어 쓰면 되기 때문에 큰 문제는 없어 보인다.

 

SimpleOnGestureListener의 선언부는 다음과 같다.

public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
            OnContextClickListener {
            .
            .
            .
}

위에서 사용한 OnGestureListener뿐 아니라, OnDoubleTapListener, OnContextClickListener또한 포함하고 있다. 이 세가지 interface를 동시에 사용할 수 있으며, 필요한 부분만 Override해서 구현하면 된다.

"땡큐 구글~"이긴 하지만, 차라리 안 만들었으면 이름때문에 헤갈릴 일은 없었을 한데...

 

DoubleTap 추가

mDetector.setOnDoubleTapListener( event handler );

DoubleTap을 추가하면 다음의 함수를 구현해야 한다.

 

boolean onSingleTapConfirmed(MotionEvent e)

onSingleTapUp에 있던 코드를 이 쪽으로 이동해야 한다. onSingleTapUp은 doubleTap일 경우 두번 발생하기 때문에, 확실히 SingleTap일 경우에 호출되는 곳이 이 곳이다.

boolean onDoubleTap(MotionEvent e)

실제 DoubleTap이 일어났을 때 호출 된다.

onDoubleTapEvent(MotionEvent e)

두번째탭이 일어난 후에 여러번 발생하며, 두번째 탭이 발생동안의 과정을 보여준다. 보통 다음과 같이 이벤트들이 들어있게되고, 그 값은 e.getActionMasked()함수로 받을 수 있다.

1. ACTION_DOWN    /   2. ACTION_MOVE(한번 이상)    /    3. ACTION_UP

특별한 상황이 아니면 구현할 필요는 없다.

 

 

 

 

Detector에는 setIsLongpressEnabled(boolean) 함수가 있는 데, long press를 지원하지 않을 경우 이 함수를 이용하여 사용하지 않는 것을 필자는 권장한다.

 

 

 

<< onTouch와 onTouchEvent Scroller >>

 

TAG
Replies
Reply Write