안드로이드 앱에서 화면을 터치하거나 키보드로 글자로 입력을 하게되면 그에 따라 정해진 반응이 일어난다. 이번 글에서는 이러한 이벤트를 처리하는 방법을 정리하고자 한다.

 

대표적인 이벤트의 유형

  • 터치 이벤트 : 화면을 손가락으로 누를 때 발생
  • 키 이벤트 : 키패드나 하드웨어 버튼을 누를 때 발생
  • 제스처 이벤트 : 터치 이벤트 중에서도 스크롤과 같이 일정 패턴으로 구분되는 이벤트
  • 포커스 : 뷰에 순서대로 주어지는 포커스
  • 화면 방향 변경 : 화면 방향이 가로와 세로로 바뀜에 따라 발생

Touch Event

안드로이드에서는 터치 이벤트를 처리할 수 있도록 클릭 이벤트(Click Event)를 별도로 제공한다.
버튼 위젯의 경우 대부분 클릭 이벤트가 필요하기 때문에 안드로이드에서 보다 간단하게 이벤트 처리를 할 수 있도록 속성과 메서드를 정해준다.

  • xml의 버튼 태그에 onClick="{메서드명}" 속성을 추가하면 클릭 이벤트가 발생했을 때 실행할 메서드를 지정할 수 있다.

  • 소스 코드에서 동일한 처리를 하려면 setOnClickListner() 메서드를 사용하면 된다.

    <Button
        android:id="@+id/btn_sign_in"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="attemptLogin"
        android:text="로그인" />

로그인 버튼을 눌렀을 때 자동으로 attempLogin 메서드를 호출하게 된다.

 

새로운 클래스에서 터치 이벤트 처리하기

클릭 이벤트는 터치 이벤트를 단순화한 것이다. 손가락으로 터치했을 때 발생하는 각각의 상태를 다루려면 View 클래스를 상속한 상태에서 onTouchEvent()를 재정의한다.

boolean onTouchEvent (MotionEvent event)

파라미터로 받아오는 MotionEvent 객체는 터치했을 때 발생한 움직임에 대한 정보를 가지고 있다.


움직임의 종류는 getAction()이나 getActionMasked() 메서드로 받아올 수 있으며, MotionEvent 안에 상수로 정의된 action code로 확인할 수 있다. 예를들어 누르는 제스처가 처음으로 확인되면 action code는 ACTION_DOWN가 된다.
getX()getY()를 사용하면 스크린에서 클릭이 발생한 위치 정보를 확인할 수 있다.

public class ViewExample extends View {

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction();

            switch (actionCode) {
                case MotionEvent.ACTION_DOWN:
                    System.out.println("손가락 눌림");
                    System.out.println("눌린 위치 : " + event.getX() + " " + event.getY());
                    break;

                case MotionEvent.ACTION_MOVE:
                    System.out.println("손가락 움직임");
                    break;

                case MotionEvent.ACTION_UP:
                    System.out.println("손가락 뗌");
                    break;
            }

            return true;
        }
    ...

 

기존의 뷰 객체에서 터치 이벤트 처리하기

기존의 뷰에서 터치 이벤트를 처리하려면 Listner를 설정하는 메서드인 setOnTouchListener()를 호출해야 한다.

View.OnTouchListener : boolean onTouch (View v, MotionEvent event) 

OnTouchListener에 정의된 onTouch()는 사용자가 손가락으로 터치를 할 때마다 발생하는 이벤트를 받아 처리한다.

view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                // 이벤트 정의
                return true;
            }
        });

 

Key Event

새로운 클래스에서 키 이벤트를 다룰 때onKeyDown()이나 onKeyUp() 메서드를 재정의한다.

boolean onKeyDown (int keyCode, KeyEvent event) 
boolean onKeyUp (int keyCode, KeyEvent event) 

사용자는 키패드로 a~z의 알파벳이나 숫자를 입력할 수도 있고, BACK이나 HOME 같은 시스템 키를 누를수도 있다.
매개변수로 들어온 keyCode를 통해 어떤 키가 눌린 건지 확인할 수 있다.
keyCode의 값은 KeyEvent 객체 안에 상수로 정의되어있다. 예를들어 KEYCODE_BACK은 뒤로가기 버튼을 의미한다.

public class ViewExample extends View {

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_5:
                System.out.println("5 입력");
                break;

            case KeyEvent.KEYCODE_C:
                System.out.println("C 입력");
                break;

            case KeyEvent.KEYCODE_VOLUME_UP:
                System.out.println("볼륨 증가 버튼 눌림");
                break;

            case KeyEvent.KEYCODE_BACK:
                System.out.println("뒤로가기 버튼 눌림");
                break;
        }

        return true;
    }

기존의 뷰에서 키 이벤트를 처리할 때OnKeyListener를 설정한다.

View.OnKeyListener : boolean onKey (View v, int keyCode, KeyEvent event) 

onKey()는 키 입력이 발생할 때마다 호출되는 메서드다.

 

BACK 버튼 이벤트

이전 화면으로 돌아가기 위해 BACK 버튼을 누르는 이벤트는 자주 발생한다. 이 경우에는 키 이벤트를 재정의할 필요 없이 onBackPressed()로 간단하게 처리가 가능하다. FragmentActivity를 상속받은 클래스에서만 사용이 가능하다.

@Override
    public void onBackPressed() {
        // 이벤트 정의

        super.onBackPressed();
    }

super.onBackPressed();가 호출된 순간 Activity가 종료되기 때문에 처리해야할 이벤트를 반드시 위쪽에 작성해야 한다.

 

Gesture Event

손가락으로 화면을 쓸어 내리거나 길게 클릭하는 등의 이벤트를 처리해야 하는 경우가 있다.
하지만 개발자가 포인터의 위치 변화를 하나하나 계산하기는 쉽지 않다. 이때 제스처 이벤트를 활용하면 손가락의 복잡한 움직임을 쉽게 처리할 수 있다. 제스처는 터치 이벤트를 받고 추가적인 확인을 거쳐 만들어진다.

 

제스처를 처리하는 클래스는 GestureDetector다. 이 객체에 터치 이벤트를 전달하면 상황에 맞는 메서드를 호출한다.

 final GestureDetector detector = new GestureDetector(context, new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent motionEvent) {
                System.out.println("화면 눌림");
                return false;
            }

            @Override
            public void onShowPress(MotionEvent motionEvent) {
                System.out.println("화면 눌렀다 떼어짐");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent motionEvent) {
                System.out.println("화면을 한손으로 눌렀다 떼어짐");
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                System.out.println("화면이 눌린채 일정한 속도와 방향으로 움직임");
                return false;
            }

            @Override
            public void onLongPress(MotionEvent motionEvent) {
                System.out.println("화면을 손가락으로 오래 누름");
            }

            @Override
            public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                System.out.println("화면이 눌린채 가속도를 붙여 움직임");
                return false;
            }
        });

        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                detector.onTouchEvent(motionEvent); // GestureDetector로 터치 이벤트를 넘긴다
                return true;
            }
        });

터치 이벤트를 감지했을 때 터치 리스너는 GestureDetector로 MotionEvent 객체를 전달한다.
그러면 GestureDetector는 속도, 방향, 위치 변화 등을 계산해 적절한 종류의 터치 이벤트 메서드를 호출한다.

 


이처럼 이벤트를 위젯 객체에 전달할 후 그 이후의 처리 과정을 위젯에 위임하는 방식을 위임 모델(Delegation Model)이라고 부른다.

위임 모델은 각각의 이벤트를 처리할 수 있는 Listner 인터페이스를 등록할 수 있도록 한다.
위임 모델을 활용하면 이벤트 처리 루틴을 각각의 뷰마다 개별적으로 할당하기 때문에 코드가 상대적으로 간단해진다.

 

참고