리스트뷰의 드래그&드롭은 android Music app의 TouchListView 소스를 살펴보면
큰 줄기는 잡을 수 있다.
해당 소스로 적용했더니 무언가를 잘못했는지 원하는 처리가 이루어지지 않았다.
drop 시의 포지션 정보가 영 이상해서..
일단, 기존 소스는 두고, 몇가지 눈에 들어오지 않는 루틴과 변수명을 정리했다.
대부분의 내용은 기존 소스와 동일.
특정 위치에 머무를 경우 자동 스크롤이 되도록 하기 위해 쓰레드를 사용할 예정인데,
요건 일단 나중으로 미뤄둠~
1. 리스트뷰를 상속받는 커스텀뷰를 만든다.
public class MyListView extends ListView {
private Context mContext;
private ImageView mDragView;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
private int mDragPos;
private int mSelectedPos;
private int mItemTopPadding;
private DragListener mDragListener;
private DropListener mDropListener;
private int mUpperBound;
private int mLowerBound;
private int mViewHeight;
private Rect mTempRect = new Rect();
private Bitmap mDragBitmap;
private final int mTouchSlop;
private int mItemHeight;
private GestureDetector mGestureDetector;
public static final int FLING = 0;
public static final int SLIDE_RIGHT = 1;
public static final int SLIDE_LEFT = 2;
private int mRemoveMode = -1;
private RemoveListener mRemoveListener;
2. 생성자. 특별한 것은 없고, 터치 move 시 스크롤로 인식하는 값을 저장해 둔다.
public MyListView (Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
3. 각 아이템으로 가는 터치 이벤트를 가로채서 down과 제스추어에 대한 이벤트 관련 전처리를 한다.
그리고, drag시 표시될 뷰를 위해 비트맵을 뷰의 drawingcache 로부터 생성한다.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// // 제스추어로 처리를 할때 사용(iphone 재생목록 처럼)
// if( mRemoveListener!=null && mGestureDetector == null ) {
// if( mRemoveMode == FLING ) {
// mGestureDetector = new GestureDetector( mContext, new SimpleOnGestureListener() {
// @Override
// public boolean onFling(MotionEvent e1, MotionEvent e2,
// float velocityX, float velocityY) {
//
// if( mDragView != null ) {
// if( velocityX > 1000 ) {
// Rect r = mTempRect;
// mDragView.getDrawingRect(r);
// if( e2.getX() > r.right * 2 / 3 ) {
// stopDragging();
// mRemoveListener.remove(mSelectedPos);
// unExpandViews(true);
// }
// }
//
// return true;
// }
//
// return false;
// }
//
// });
// }
// }
if (mDragListener != null || mDropListener != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) ev.getX();
int y = (int) ev.getY();
int itemPosition = pointToPosition(x, y);
if (itemPosition == AdapterView.INVALID_POSITION) {
break;
}
View item = getChildAt(itemPosition - getFirstVisiblePosition());
mItemHeight = item.getHeight();
mItemTopPadding = y - item.getTop();
Rect r = mTempRect;
item.getDrawingRect(r);
if((x> r.left) && (x < r.right ) ) {
item.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
startDragging(bitmap, (int)ev.getRawY() - mItemTopPadding);
mDragPos = itemPosition;
mSelectedPos = itemPosition;
mViewHeight = getHeight();
mUpperBound = Math.min(y - mTouchSlop, mViewHeight / 3);
mLowerBound = Math.max(y + mTouchSlop, mViewHeight - (mViewHeight / 3));
return false;
}
mDragView = null;
break;
}
}
return super.onInterceptTouchEvent(ev);
}
4. 실제 터치 이벤트에 대한 세부적인 처리
@Override
public boolean onTouchEvent(MotionEvent ev) {
// if(mGestureDetector != null ) {
// mGestureDetector.onTouchEvent(ev);
// }
if ((mDragListener == null || mDropListener == null) && mDragView == null) {
return super.onTouchEvent(ev);
}
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
Rect r = mTempRect;
mDragView.getDrawingRect(r);
stopDragging();
if( mRemoveMode == SLIDE_RIGHT && ev.getX() > r.left + (r.width()*3/4)) {
// if( mRemoveListener != null ) {
// mRemoveListener.remove(mSelectedPos);
// }
// unExpandViews(true);
} else if( mRemoveMode == SLIDE_LEFT && ev.getX() < r.right+(r.width()/4)) {
// if( mRemoveListener != null ) {
// mRemoveListener.remove(mSelectedPos);
// }
// unExpandViews(true);
} else {
if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
int y = (int) ev.getY();
int curPos = mDragPos - getFirstVisiblePosition();
View v = getChildAt(curPos);
int top = y - mItemTopPadding;
if( v.getTop() > top ) {
if( mSelectedPos < mDragPos )
mDropListener.drop(mSelectedPos, mDragPos - 1);
else
mDropListener.drop(mSelectedPos, mDragPos );
} else {
if( mSelectedPos < mDragPos )
mDropListener.drop(mSelectedPos, mDragPos);
else
mDropListener.drop(mSelectedPos, mDragPos + 1 );
}
}
unExpandViews(false);
}
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) ev.getX();
int y = (int) ev.getY();
dragView(x, (int)ev.getRawY() - mItemTopPadding);
int itemPosition = pointToPosition(x, y);
if (itemPosition >= 0) {
if (action == MotionEvent.ACTION_DOWN || itemPosition != mDragPos) {
if (mDragListener != null) {
mDragListener.drag(mDragPos, itemPosition);
}
mDragPos = itemPosition;
doExpansion();
}
int speed = 0;
if (y > mLowerBound) {
speed = y > mLowerBound + mUpperBound / 2 ? 32 : 8;
} else if (y < mUpperBound) {
speed = y < mUpperBound / 2 ? -32 : -8;
}
if (speed != 0) {
int ref = pointToPosition(0, mViewHeight / 2);
if (ref == AdapterView.INVALID_POSITION) {
ref = pointToPosition(0, mViewHeight / 2 + getDividerHeight() + 8);
}
View v = getChildAt(ref - getFirstVisiblePosition());
if (v!= null) {
int pos = v.getTop();
setSelectionFromTop(ref, pos - speed);
}
}
}
break;
}
return true;
}
5. 항목이 클릭 되었을 때 리스트 뷰의 처리
클릭했을때 해당 항목이 보이지 않는 정도의 기능만 필요해서 해당 기능만 추가.
private void doExpansion() {
int position = mDragPos - getFirstVisiblePosition();
int itemPos = mSelectedPos - getFirstVisiblePosition();
int count = getChildCount();
int height;
int visibility;
for( int i=0; i<count; i++ ) {
View v = getChildAt(i);
if( v == null ) continue;
height = mItemHeight;
visibility = View.VISIBLE;
// 드래그한 포지션이 현재 화면에 보이는 경우
if( position == i ) {
if( mDragPos == mSelectedPos ) {
visibility = View.INVISIBLE;
height = 1;
} else {
}
} else if( itemPos == i ) {
visibility = View.INVISIBLE;
height = 1;
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = height;
v.setLayoutParams(params);
v.setVisibility(visibility);
}
}
6. 그외 변경사항 없음.
private void unExpandViews(boolean deletion) {
for (int i = 0;; i++) {
View v = getChildAt(i);
if (v == null) {
if (deletion) {
int position = getFirstVisiblePosition();
int y = getChildAt(0).getTop();
setAdapter(getAdapter());
setSelectionFromTop(position, y);
}
layoutChildren();
v = getChildAt(i);
if (v == null) {
break;
}
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = mItemHeight;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
}
}
private void startDragging(Bitmap bm, int y) {
stopDragging();
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
ImageView v = new ImageView(mContext);
int backGroundColor = Color.parseColor("#e0103010");
v.setBackgroundColor(backGroundColor);
v.setImageBitmap(bm);
mDragBitmap = bm;
mWindowManager=(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(v, mWindowParams);
mDragView = v;
}
private void dragView(int x, int y) {
// float alpha = 1.0f;
// int width = mDragView.getWidth();
//
// if( mRemoveMode == SLIDE_RIGHT ) {
// if (x > width / 2) {
// alpha = ((float)(width - x)) / (width / 2);
// }
// mWindowParams.alpha = alpha;
// } else if( mRemoveMode == SLIDE_LEFT ) {
// if (x < width / 2) {
// alpha = ((float)x) / (width / 2);
// }
// mWindowParams.alpha = alpha;
// }
mWindowParams.y =y;
mWindowManager.updateViewLayout(mDragView, mWindowParams);
}
private void stopDragging() {
if (mDragView != null) {
WindowManager wm = (WindowManager)mContext.getSystemService("window");
wm.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
}
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
}
public void setOnDragListener(DragListener l) {
mDragListener = l;
}
public void setOnDropListener(DropListener l) {
mDropListener = l;
}
public void setRemoveListener(RemoveListener l) {
mRemoveListener = l;
}
public interface DragListener {
void drag(int from, int to);
}
public interface DropListener {
void drop(int from, int to);
}
public interface RemoveListener {
void remove(int which);
}
'프로그래밍 > Android' 카테고리의 다른 글
천천히 로딩되는 리스트 아이템 (0) | 2011.08.22 |
---|---|
이미지 클리핑 기초 (0) | 2011.08.18 |
ffmpeg 빌드 #3 (0) | 2011.07.07 |
ffmpeg 빌드 #2 ffmpeg 옵션별 빌드 (0) | 2011.07.06 |
ffmpeg 라이브러리 빌드 #1 (0) | 2011.06.30 |
뷰 전환시 3차원 에니메이션 적용하기 (0) | 2010.09.17 |
안드로이드 위젯 배경 이미지, 나인패치 (0) | 2010.08.17 |
옵션메뉴 배경 변경 (0) | 2010.08.10 |
갤러리, 커버플로우 구현 (0) | 2010.07.27 |
AppWidget 여러 개의 위젯을 가진 앱위젯 (0) | 2010.07.09 |