Android: Touch Event

ericbyeric·2021년 6월 29일
0

Android

목록 보기
6/6
post-thumbnail

Touch Event

How to implement touch event in android?

Android에서 touch기능을 구현할 때 2가지 방법이 있어

  1. Override onTouchEvent()
    • View 클래스로부터 상속
  2. implement View.OnTouchListener
    • 상속을 받지 않고 사용할 수 있다는 장점

동작순서

  1. View.OnTouchListener를 implement했을 경우()

    • OnTouch가 실행 추가적으로 onTouchEvent를 호출 할지 말지를 결정할 수 있어.
  2. onTouchEvent를 override했을 경우

    • onTouchEvent 호출

호출방식
화면을 누르거나, 뗴거나, 닿은채로 움직이거나 할때 onTouch / onTouchEvent가 호출되.

1. Override onTouchEvent (View를 Extend하는 경우)

View는 onTouchEvent라는 함수를 가지고 있어. View의 Child들은 전부 onTouchEvent를 사용할 수 있어.

우리는 View를 extend해서 onTouchEvent를 override할꺼야.

class touchEventExtendView extends View
{
	@Override
    public boolean onTouchEvent(MotionEvent event)
    {
    	boolean isOnePointTouch = false;
        
        float x = event.getX();
        float y = eventgetY();
        
        switch(event.getActionMasked())
        {
        	// 화면을 손가락으로 터치할떄, 모든 Event의 출발
        	case MotionEvent.ACTION_DOWN:
            		
                    // 다음에 발생할 후속 Event가 필요하다는 의미
                    isOnePointTouch = true;
                	break;
            	// 화면에서 손가락을 땠을경우, Event의 끝
                case MotionEvent.ACTION_UP:
                	// click임으로, OnClickListener를 호출
                    perfomClick();
                    
                    isOnePointTouch = true;
                    break;
                    
                // 화면에 손가락이 닿은 상태로 움직이고 있는 Event(움직일때마다 호출되)    
                case MotionEvent.ACTION_MOVE:
                	
                    // false를 return한다고해서 ACTION_MOVE나 ACTION_UP Event가 안들어오는게 아니야.
                    // 사실상 의미없는 return값이야
                    isOnePointTouch = true;
                    break;
                	
        }
        return ret;
    }
}

2. implement View.OnTouchListener

사실 class 자체에서 View.OnTouchListener를 implement하는방식이나 특정 Activity에서 findViewById()로 특정 View를 가져와서 setOnTouchListener(this)를 호출하는 방식이나 똑같이 onTouch 메서드를 override하게끔 되어있어.

밑의 예시를 보자.

class MainActivity extens AppCompatActivity
{
    .
    .
	@Override
    protected void onCreate(Bundle savedInstanceState)
    {
    	super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        .
        .
        // Touch listener 지정
        findViewById(R.id.touch_screen).setOnTouchListener(this);
        .
        .
    }
    
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent)
    {
    	boolean isOnePointTouch = false;
        
        if(view.getId() == R.id.touch_screen)
        {
        	float x = motionEvent.getX();
            	float y = motionEvent.getY();
            
            switch( motionEvent.getActionMasked() )
            {
            	case MotionEvent.ACTION_DOWN:
                	isOnePointTouch = false;
                    break;
                    
                case MotionEvent.ACTION_UP:
                    // Click이 됨을 나타냄. OnClickListener를 호출
                    isOnePointTouch = false;
                    break;
                    
                case MotionEvent.ACTION_MOVE:
                    isOnePointTouch = true
                    break;
            }
        }
        
    }
}

다양한 View에서 Touch event가 발생할수 있기 때문에 해당 View의 id로 구분지어서 처리해야해.
현재 MainActivity에서 여러 View Listener가 호출될 수 있기에 구분해 줘야되 각 View의 id로.

단일 View의 터치 event캡쳐
findViewById(R.id.sampleView).setOnTouchListener( new View.OnTouchListener() {...} 처럼 코드를 작성하면 이건 이미 특정 View에서만 Touch event가 작동(호출)되기 떄문에 구분지을 필요가 없어.

onTouch & onTouchEvent의 Return값이 어떤 의미가 있을까?

위의 예시 에서처럼 true를 return한다면 "후속 Event를 보내" & "현재 event 처리했어, 이번 event에 대해 추가작업 하지마" 이런 의미야.

false는? true의 반대겠지? 하지만 확 감이안오니 예를 들어볼꼐

위에서 말했듯이 setOnTouchListener가 기본적으로 지정한 Handler인onTouch가 먼저 호출되고, View의 onTouchEvent가 호출되(이건 유저가 선택)

핸들러 함수(onTouch)에 ACTION DOWN event가 들어오면

  • return true의 경우
    - View의 onTouchEvent함수 호출안됨 (이번 event에 대해 추가작업 하지마)
    - return true 이기에 Android OS는 다음에 발생할 ACTION MOVE와 ACTION UP 등의 이벤트를 받으면 다시 onTouch를 호출해줘 (후속 event가 필요하니 보내라)
  • return false의 경우
    - View의 onTouchEvent함수 호출됨
    - 만약 onTouchEvent와 다른 작업에서 true가 되면, 후속 event가 필요하다는 의미이기 떄문에 다음 event (e.g. ACTION MOVE, ACTION UP등)가 다시 발생하면 onTouch가 호출되.
    - false를 return하고 onTouch 및 다른 작업들에서 최종으로 false가 되면, OS는 후속 event에 대해서 onTouch도 onTouchEvent도 호출안해

간단히 말하면
true가 리턴 : 다음에 올 event가 필요해!
false가 리턴 : 다음에 event 안와도되~

재미있게도 ACTION DOWN event에서 최종적으로 false를 return하게 되면 말 그대로 "아무것도 안할꺼야" 라는 의미가되. ACTION DOWN은 모든 event의 시작이거든, 그래서 이떄의 return값이 중요해.

Motion Event

한 포인트 터치 구현

public class MainActivity extends Activity
{
  ...
  @Override
  public boolean onTouchEvent(MotionEvent event)
  {
      int action = MotionEventCompat.getActionMasked(event);
      switch(action)
      {
	   case MotionEvent.ACTION_DOWN:
       		x = event.getX();
            	y = event.getY();
            	break;
            case MotionEVent.ACTION_MOVE:
            	x = event.getX();
                y = event.getY();
                break;
      }
      return true;
  }

}

발생한 event를 마스킹해서 어떤 Motion event(e.g. ACTION DOWN, ACTION MOVE..)가 발생했는지 확인해서 그에 맞는 처리를 해.

두 개의 포인트 터치 구현

@Override
public boolean onTouchEvent(MotionEvent event)
{
	// 터치가 발생된 포인트 수 (e.g. 화면에 손가락 몇개가 터치됐냐)
	int pointer_cnt = event.getPointerCount();
    	if(pointer_cnt > 2) 
        {
        	// 터치를 2개 포인트(손가락 2개)까지만 처리함
    		pointer_cnt = 2;
	}
    
    	int action = MotionEventCompat.getActionMasked(event);
        
        switch(action)
        {
        	case MotionEvent.ACTION_DOWN:
            		// 한개의 포인트에 대한 DOWN을 얻을때
            	   	num[0] = event.getPointerId(0); // 터치한 순간부터 부여받는 포인트 고유번호
                    	x[0] = event.getX();
                    	y[0] = event.getY();
                        break;
                        
            	case MotionEvent.ACTION_POINTER_DOWN:
                	// 두개 이상의 포인트에 대한 DOWN을 얻을때
                	for(int i = 0; i < pointer_cnt; i++)
                        {
                        	num[i] = event.getPointerId(i);
                            	x[i] = event.getX(i);
                                y[i] = event.getY(i);
                        }
                        break;
                        
                case MotionEvent.ACTION_MOVE:
                	for(int i = 0; i < pointer_cnt; i++)
                    	{
                        	num[i] = event.getPointerId(i);
                            	x[i] = event.getX(i);
                                y[i] = event.getY(i);
                        }
                        break;
        }
        return true;
}

event.getPointerCount() 는 손가락 2개로 터치했다면 2를 return해줘.
2개의 포인트만 처리할 것이라 3개 이상의 터치가 인식되더라도 최근 2 포인트만 처리하도록 코드를 작성했어.

앱에서 1개의 포인트 & 2개 이상의 포인트 터치를 모두 처리해 주어야 하면 두개의 이벤트 (ACTION DOWN, ACTION POINTER DOWN) 모두 구현 해주어야해.

그럼 각각의 event에서 움직일떄는?
럭키하게 ACTION_MOVE event가 1 포인트나 2 포인트 모두 같은 ACTION_MOVE event로 들어오게되. (편하당)

event.getPointId(i)로 각 포인터에 대한 고유번호는 따로따로 저장해줘. Why?
2 포인트가 인식 되었다고 가정해보자.

num[0] = 0
x[0] = 50
y[0] = 50

num[1] = 1
x[0] = 180
y[0] = 180

첫번째 손가락을 떼면 어떻게 될까?

num[0] = 1
x[0] = 180
y[0] = 180

num[1] = 0
x[0] = 0
y[0] = 0

Index 1에 있던 배열이 0번쨰 배열로 넘어갔지?
이게 의미하는 바는 배열의 순서가 바뀌더라도 터치 상황을 문제없이 인식할수 있어 고유번호 덕분에.

그래서 다중 터치구현시 배열의 인덱스 기준이 아니라, 개별 포인트 고유 아이디값을 확인해서 처리해야해

정리:
ACTION_DOWN: 터치 포인트 1개
ACTION_POINTER_DOWN: 터치 포인트 2개 이상
다중 터치도중 하나 이상의 터치 포인트가 해제되면 좌표 데이터를 담고있는 배열의 순서가 바뀐다. 그래서 터치 포인터의 고유번호를 비교하는 방식으로 구현한다!

profile
인생의 해상도를 올려볼까나

0개의 댓글