본문 바로가기

프로그래밍/Android

Drag&drop listview

 

 

리스트뷰의 드래그&드롭은 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);

}