본문 바로가기 메뉴 바로가기

티스토리 뷰




 < 개발 환경 >  
   작성일 : 2013.01.27
   OS 설치 버전 : Windows7 32bit  
   SDK 설치 버전 : 안드로이드 SDK 4.2 (젤리빈) / API LEVEL : 17  
   ADT 설치 버전 : 21   
   Java 설치 버전 : JDK 1.6.0_20 / JRE6 
   이클립스 설치 버전 : Indigo
   테스트단말 : 삼성 갤럭시 S2 4.0.4 (아이스크램 샌드위치)   

 < 프로젝트 적용 > 
   Android Build Target / API LEVEL / Complie With : 17  
   minSdkVersion : 8 
   targetSdkVersion : 16  
   Java Compiler Level : 1.6  
   Text file encoding : UTF-8







안드로이드/Android 터치 이벤트(Touch Event) 에서 Touch, LongTouch 동시에 구현 하기 ~!





안드로이드 프로그래밍을 진행하다면 보면 View에 Touch와 LongTouch Listener를 동시에 구현해야 하는 경우가 생기는 데요. Touch와 LongTouch Listener 동시에 구현하는 방법에 대해 알아 보겠습니다. 예제 코드는 아래의 원본 포스팅의 내용으로 작성 하였습니다.




<원본내용>



안녕하세요.


안드로이드의 콤포넌트들은 기본적으로 Long Click을 지원합니다.


그래도, 가끔은 Long Click 자체를 만들어야 할 때가 있더군요.


우선적으로 기본 View.OnLongClickListener 를 사용하시구요.

http://developer.android.com/reference/android/view/View.OnLongClickListener.html


이것을 못 쓸 경우는 아래와 같이 만드시면 된답니다.



우선 마우스 이벤트만을 이용해서 Long Click을 구현하는 방법은 간단합니다.


* 마우스 down

- 초기 위치와 시간 기억


* 마우스 Move

- 일정범위 벗어나면 취소


* 마우스 up

- 일정시간(long click time) 이 초과했으면 , Long click

- 시간이 안 지났으면 , short click



이 방법의 문제점은 사용자가 클릭을 떼지 않으면 long click 이 발생하지 않는 다는 것입니다.

예들 들어, long click time을 600ms 로 주었다고 해도, 누른체로 3초를 누르고 있어도 떼지 않으면 long click 이벤트가 발생하지 않게 되지요.



개선된 방법은 아래와 같습니다.


아이디어는 간단한데요.


* 마우스 down

- 초기 위치 기억

- delayedMassage를 생성합니다.


* 마우스 Move

- 일정범위 벗어나면 취소


* 마우스 up

- long click을 처리 안 되었으면 message를 지우고, Shot Click을 수행합니다.


* message 함수

- Long Click을 처리합니다.


위와 같이 수정합니다.



// 시작 위치를 저장을 위한 변수

private float mLastMotionX = 0;

private float mLastMotionY = 0;

// 마우스 move 로 일정범위 벗어나면 취소하기 위한 값

private int mTouchSlop;

// long click 을 위한 변수들

private boolean mHasPerformedLongPress;

private CheckForLongPress mPendingCheckForLongPress;

우선, 마우스의 최소 이동 범위를 구해야 하는데요.

안드로이드에서 drag 범위를 인식하는 기본값이 있답니다.


mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();


위와 같이 구하면 되구요.


Long Click을 처리하는 루틴을 만듭니다.


// Long Click을 처리할 Runnable 입니다.

class CheckForLongPress implements Runnable {


public void run() {

if (performLongClick()) {

mHasPerformedLongPress = true;

}

}

}

// Long Click 처리 설정을 위한 함수

private void postCheckForLongClick(int delayOffset) {

mHasPerformedLongPress = false;


if (mPendingCheckForLongPress == null) {

mPendingCheckForLongPress = new CheckForLongPress();

}

mHandler.postDelayed(mPendingCheckForLongPress,

ViewConfiguration.getLongPressTimeout() - delayOffset);

// 여기서 시스템의 getLongPressTimeout() 후에 message 수행하게 합니다.

// 추가 delay가 필요한 경우를 위해서 파라미터로 조절가능하게 합니다.

}


/**

* Remove the longpress detection timer.

* 중간에 취소하는 용도입니다.

*/

private void removeLongPressCallback() {

if (mPendingCheckForLongPress != null) {

mHandler.removeCallbacks(mPendingCheckForLongPress);

}

}

public boolean performLongClick() {

// 실제 Long Click 처리하는 부분을 여기 둡니다.

return true;

}

위와 같이 코드를 만들어 두시구요.


실제 사용할 부분은 onTouchEvent를 구현해 두면 된답니다.





public boolean onTouch(View v, MotionEvent event) {

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

mLastMotionX = event.getX();

mLastMotionY = event.getY(); // 시작 위치 저장


mHasPerformedLongPress = false;

postCheckForLongClick(0); // Long click message 설정

break;

case MotionEvent.ACTION_MOVE:

final float x = event.getX();

final float y = event.getY();

final int deltaX = Math.abs((int) (mLastMotionX - x));

final int deltaY = Math.abs((int) (mLastMotionY - y));

// 일정 범위 벗어나면 취소함

if (deltaX >= mTouchSlop || deltaY >= mTouchSlop) {

if (!mHasPerformedLongPress) {

// This is a tap, so remove the longpress check

removeLongPressCallback();

}

}


break;

case MotionEvent.ACTION_CANCEL:

if (!mHasPerformedLongPress) {

// This is a tap, so remove the longpress check

removeLongPressCallback();

}

break;

case MotionEvent.ACTION_UP:

if (!mHasPerformedLongPress) {

// Long Click을 처리되지 않았으면 제거함.

removeLongPressCallback();

// Short Click 처리 루틴을 여기에 넣으면 됩니다.


}

break;

}

return true; // false;

}


위와 같이 구현 하시면 되구요.


Long Click 취소 루틴을 직접 테스트하면 적절한 곳에 추가해 주시면됩니다.


감사합니다. 






이상 원본 내용의 포스팅 이였구요. 포스팅을 바탕으로 직접 실행되는 예제를 만들어 봤습니다. 아래의 예제를 살펴 보겠습니다.


package arabiannight.tistory.com.longtouchevent;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

	// 시작 위치를 저장을 위한 변수 
	private float mLastMotionX = 0;
	private float mLastMotionY = 0;
	// 마우스 move 로 일정범위 벗어나면 취소하기 위한 값
	private int mTouchSlop;

	// long click을 위한 변수들 
	private boolean mHasPerformedLongPress;
	private CheckForLongPress mPendingCheckForLongPress;

	private Handler mHandler = null;

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

		mHandler = new Handler();

		mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();

		Button btn_Long_Touch = (Button) findViewById(R.id.btn_1);
		

		btn_Long_Touch.setOnTouchListener(new View.OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {

				switch (event.getAction()) {

				case MotionEvent.ACTION_DOWN:
					Log.d("CLICK", "ACTION_DOWN");

					mLastMotionX = event.getX();
					mLastMotionY = event.getY();   // 시작 위치 저장

					mHasPerformedLongPress = false;   
					
					postCheckForLongClick(0);     //  Long click message 설정

					break;

				case MotionEvent.ACTION_MOVE:
					Log.d("CLICK", "ACTION_MOVE");	

					final float x = event.getX();
					final float y = event.getY();
					final int deltaX = Math.abs((int) (mLastMotionX - x));
					final int deltaY = Math.abs((int) (mLastMotionY - y));

					// 일정 범위 벗어나면  취소함
					if (deltaX >= mTouchSlop || deltaY >= mTouchSlop) {
						if (!mHasPerformedLongPress) {
							// This is a tap, so remove the longpress check
							removeLongPressCallback();
						}
					}

					break;

				case MotionEvent.ACTION_CANCEL:
					if (!mHasPerformedLongPress) {
						// This is a tap, so remove the longpress check
						removeLongPressCallback();
					}
					break;

				case MotionEvent.ACTION_UP:
					Log.d("CLICK", "ACTION_UP");

					if (!mHasPerformedLongPress) {
						// Long Click을 처리되지 않았으면 제거함.
						removeLongPressCallback();

						// Short Click 처리 루틴을 여기에 넣으면 됩니다.
						performOneClick(); 

					}

					break;

				default:
					break;
				}

				return false;
			}
		});

	}

	// Long Click을 처리할  Runnable 입니다. 
	class CheckForLongPress implements Runnable {

		public void run() {
			if (performLongClick()) {
				mHasPerformedLongPress = true;
			}
		}
	}

	// Long Click 처리 설정을 위한 함수 
	private void postCheckForLongClick(int delayOffset) {
		mHasPerformedLongPress = false;

		if (mPendingCheckForLongPress == null) {
			mPendingCheckForLongPress = new CheckForLongPress();
		}

		mHandler.postDelayed(mPendingCheckForLongPress,
				ViewConfiguration.getLongPressTimeout() - delayOffset);
		// 여기서  시스템의  getLongPressTimeout() 후에 message 수행하게 합니다.  
		// 추가 delay가 필요한 경우를 위해서  파라미터로 조절가능하게 합니다.
	}


	/**
	 * Remove the longpress detection timer.
	 * 중간에 취소하는 용도입니다.
	 */
	private void removeLongPressCallback() {
		if (mPendingCheckForLongPress != null) {
			mHandler.removeCallbacks(mPendingCheckForLongPress);
		}
	}

	public boolean performLongClick() {
		//  실제 Long Click 처리하는 부분을 여기 둡니다.
		Log.d("CLICK", "Long Click OK");
		Toast.makeText(MainActivity.this, "Long Click OK!!", Toast.LENGTH_SHORT).show();
		return true;
	}
	
	private void performOneClick() {
		Log.d("CLICK", "One Click OK");
		Toast.makeText(MainActivity.this, "One Click OK!!", Toast.LENGTH_SHORT).show();
	}

}





파일첨부 : 


TestLongTouchEvent.zip



스크린샷 : 










댓글
  • 프로필사진 ze.law 정말 많은 도움이 되었습니다. 감사합니다.
    그런데 궁금 한 것이 '추가 delay가 필요한 경우를 위해서 파라미터로 조절가능하게 합니다.'
    이 부분이 이해가 안 가는데, 혹 설명 해 주실 수 있나요?
    2015.10.30 11:30 신고
댓글쓰기 폼