[Android] ListView에서 Single Tap, Double Tap 사용하기

Published on: 2013. 11. 14. 16:32 by louis.dev
리스트뷰에서 단순 탭과 더블탭을 구분하여 실행하는 방법을 찾던 중  github에서 괜찮은 코드를 발견했습니다. 그런데 이 코드에서 버그가 발견되어 버그를 수정하여 해당 소스를 공유하려고 합니다. 버그 내용은 싱글 탭을 하여 액티비티 전환이 일어난 후(선택된 아이템을 상세보기 하기 위해), 다시 뒤로 돌아와 동일한 아이템을 한번 더 누르면 더블 탭으로 인식하는 버그였습니다. 버그를 수정하기 위한 코드는 한줄이지만 전체 소스를 공유해 봅니다.
public class TabListView extends ListView {
	private Context mContext;

	// 더블텝을 사용하기 위한 변수들
	private boolean mFirstTabEvent;
	private Message mTapMessage;
	private final static int DOUBLE_TAP = 2;
	private final static int SINGLE_TAP = 1;
	private AdapterView<?> mParent;
	private View mView;
	private int mPosition;
	private long mId;
	private final static int DOUBLE_TAP_DELAY = ViewConfiguration.getDoubleTapTimeout();
	private int mPositionHolder = -1;

	private OnItemTapLister mItemTapListener;

	public TabListView(Context context, AttributeSet attrs,int defStyle) {
		super(context, attrs, defStyle);
		initConstructor(context);
	}

	public TabListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initConstructor(context);
	}

	public TabListView(Context context) {
		super(context);
		initConstructor(context);
	}

	private void initConstructor(Context context) {
		this.mContext = context;
		super.setOnItemClickListener(mItemClickListener);
	}

	/**
	 * 리스트뷰 더블텝 이벤트
	 * */
	public void setOnItemTapListener(OnItemTapLister l) {
		this.mItemTapListener = l;
	}

	OnItemClickListener mItemClickListener = new OnItemClickListener() {
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
			mParent = parent;
			mView = view;
			mPosition = position;
			mId = id;

			if (!mFirstTabEvent) {
				mPositionHolder = position;
				mFirstTabEvent = true;
				mTapMessage = mTapMessage == null ? new Message() : mHandler
						.obtainMessage();
				mHandler.removeMessages(SINGLE_TAP);
				mTapMessage.what = SINGLE_TAP;
				mHandler.sendMessageDelayed(mTapMessage, DOUBLE_TAP_DELAY);
			} else {
				if (mPositionHolder == position) {
					mHandler.removeMessages(SINGLE_TAP);
					mPosition = position;
					mTapMessage = mHandler.obtainMessage();
					mTapMessage.what = DOUBLE_TAP;
					mHandler.sendMessageAtFrontOfQueue(mTapMessage);
					mFirstTabEvent = false;
				} else {
					mTapMessage = mHandler.obtainMessage();
					mHandler.removeMessages(SINGLE_TAP);
					mFirstTabEvent = true;
					mTapMessage.what = SINGLE_TAP;
					mPositionHolder = position;
					mHandler.sendMessageDelayed(mTapMessage, DOUBLE_TAP_DELAY);
				}
			}
		}

	};

	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch (msg.what) {
			case SINGLE_TAP:
				mFirstTabEvent = false;//이 코드르 삽입함으로서 해당 버그 해결
				mItemTapListener.OnSingleTap(mParent, mView, mPosition, mId);
				break;
			case DOUBLE_TAP:
				mItemTapListener.OnDoubleTap(mParent, mView, mPosition, mId);
				break;
			}
		}

	};
	//Tab 이벤트 인터페이스
	public interface OnItemTapLister {
		public void OnDoubleTap(AdapterView<?> parent, View view, int position,long id);
		public void OnSingleTap(AdapterView<?> parent, View view, int position,long id);
	}

}
사용법은 다음처럼 하면 됩니다.
tabListView.setOnItemTapListener(new SingleColumnListView.OnItemTapLister() {
	@Override
	public void OnSingleTap(AdapterView<?> parent, View view, int position,long id) {
		//TODO 싱글 탭 시 작업
	}
	@Override
	public void OnDoubleTap(AdapterView<?> parent, View view, int position,long id) {
		/TODO 더블 탭 시 작업
	}
});
차~~암 쉽죠~?ㅎㅎ

[Android] ListView에서 OnItemClick 이벤트가 실행되지 않을때

Published on: 2013. 11. 14. 16:32 by louis.dev
ListView에서 각 row를 선택할때 수행되는 이벤트가 OnItemClick이라는 이벤트가 발생하는데요. 어떠한 경우에는 리스트의 아이템을 아무리 클릭하여도 OnItemClick이 수행되지 않는 경우가 있습니다. 원인은 ListView를 구성하는 row중에 auto focusing이 되는 Widget들이 들어있는 경우에 OnItemClick이 실행이 되지 않습니다. 대표적으로 auto focusing이 되는 위젯들은 안드로이드의 Button, EditText 가 있는데요, 이럴경우엔 Button 또는 EditText를 선언한 xml에
android:focusable ="false"
을 추가해 주시면 됩니다. 이렇게 간단하게 OnItemClick을 수행할수 있도록 할수 있습니다.

[Android] 리스트뷰에서 스크롤 바닦에 닿았을때 체크하기

Published on: 2013. 11. 14. 16:31 by louis.dev
이번에는 리스트뷰에서 스크롤이 바닦에 닿았을때를 체크하는 방법을 포스팅 해보겠습니다. 이런 방법으로 스크롤의 상태를 체크하게 되면 스크롤이 최하단으로 내려갔을때 자동으로 다음 페이지의 데이터를 가져온다던가 하는(일명 무한스크롤) 일을 할수 있겠죠?  
boolean lastitemVisibleFlag = false;		//화면에 리스트의 마지막 아이템이 보여지는지 체크
listview.setOnScrollListener(new AbsListView.OnScrollListener() {
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		//현재 화면에 보이는 첫번째 리스트 아이템의 번호(firstVisibleItem) + 현재 화면에 보이는 리스트 아이템의 갯수(visibleItemCount)가 리스트 전체의 갯수(totalItemCount) -1 보다 크거나 같을때
		lastItemVisibleFlag = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount;		
	}	
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		 //OnScrollListener.SCROLL_STATE_IDLE은 스크롤이 이동하다가 멈추었을때 발생되는 스크롤 상태입니다. 
		 //즉 스크롤이 바닦에 닿아 멈춘 상태에 처리를 하겠다는 뜻
		 if(scrollState == OnScrollListener.SCROLL_STATE_IDLE && lastItemVisibleFlag) {
			//TODO 화면이 바닦에 닿을때 처리
		} 
	}

});
위처럼 스크롤이벤트를 통해서 쉽게 리스트의 끝을 체크할수 있습니다.

[Android] ListView에서 스크롤 방향 체크하기

Published on: 2013. 11. 14. 16:31 by louis.dev

ListView에서 스크롤 방향 체크하기

스크롤뷰, 혹은 리스트뷰에서 터치이벤트를 이용해서 스크롤이 위, 아래중 어디로 움직이는지 확인하는 방법을 포스팅해 보겠습니다.
boolean firstDragFlag = true;
boolean dragFlag = false;	//현재 터치가 드래그 인지 확인
float startYPosition = 0;		//터치이벤트의 시작점의 Y(세로)위치
listView.setOnTouchListener(new View.OnTouchListener() {
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_MOVE:		//터치를 한 후 움직이고 있으면
			dragFlag = true;
			if(firstDragFlag) {		//터치후 계속 드래그 하고 있다면 ACTION_MOVE가 계속 일어날 것임으로 무브를 시작한 첫번째 터치만 값을 저장함
				startYPosition = ev.getY();	//첫번째 터치의 Y(높이)를 저장
				firstDragFlag= false;	//두번째 MOVE가 실행되지 못하도록 플래그 변경
			}

			break;

		case MotionEvent.ACTION_UP : 
			endYPosition = ev.getY();
			firstDragFlag= true;

			if(dragFlag) { 	//드래그를 하다가 터치를 실행
				// 시작Y가 끝 Y보다 크다면 터치가 아래서 위로 이루어졌다는 것이고, 스크롤은 아래로내려갔다는 뜻이다.
				// (startYPosition - endYPosition) > 10 은 터치로 이동한 거리가 10픽셀 이상은 이동해야 스크롤 이동으로 감지하겠다는 뜻임으로 필요하지 않으면 제거해도 된다.
				if((startYPosition > endYPosition) && (startYPosition - endYPosition) > 10) {
					//TODO 스크롤 다운 시 작업
				} 
				//시작 Y가 끝 보다 작다면 터치가 위에서 아래로 이러우졌다는 것이고, 스크롤이 올라갔다는 뜻이다.
				else if((startYPosition < endYPosition) && (endYPosition - startYPosition) > 10) {
					//TODO 스크롤 업 시 작업
				}
			}

			startYPosition = 0.0f;
			endYPosition = 0.0f;
			motionFlag = false;
			break;
		}
		return false;
	}
});
이렇게 터치 이벤트를 통해서 스크롤의 진행방향을 체크하면 됩니다.

[Android]- java code로 view.getWidth(), view.getHeight()가 0이 나올때

Published on: 2013. 11. 14. 16:30 by louis.dev
Java Code로 어떤 View의 폭과 높이를 구할때 view.getWidth(), view.getHeight() method를 통해 값을 가져올수 있습니다. 그런데 사용하는 위치에 따라 해당 값들이 모두 0으로 리턴이 될때가 있습니다. getWidth(), getHeight()가 0으로 리턴되는 이유는 간단함니다. 해당 view가 아직 화면에 그려지지 않고 있기 때문에 getWidth, getHeight가 0이 나오게 되는것입니다. 바꾸어 말하자면
view가 화면에 그려져 있지 않다면 getWidth(), getHeight()는 0이 리턴
이 된다는 말입니다. 그런데 때에 따라서 view가 화면에 그려지기 전에 width와 height를 구해야 한다면 다음과 같이 하면 됩니다.
view.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);

int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
이렇게 하면 width와 height를 구할수 있게 됩니다.