기본 콘텐츠로 건너뛰기

안드로이드 Drag and Drop

아래내용을 사용한 예제가 있는곳:
http://blog.naver.com/PostView.nhn?blogId=monk773&logNo=90145409278

출처:http://codingdojo.kr/scode/322
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.pyframe.tools.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;

import com.mytodo.andriod.R;

public class DndListView extends ListView {

    private Context mContext;
    private ImageView mDragView;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mWindowParams;
    private int mDragPos;      // which item is being dragged
    private int mFirstDragPos; // where was the dragged item originally
    private int mDragPoint;    // at what offset inside the item did the user grab it
    private int mCoordOffset;  // the difference between screen coordinates and coordinates in this view
    private DragListener mDragListener;
    private DropListener mDropListener;
//    private RemoveListener mRemoveListener;
    private int mUpperBound;
    private int mLowerBound;
    private int mHeight;
    private GestureDetector mGestureDetector;
//    private static final int FLING = 0;
//    private static final int SLIDE = 1;
//    private int mRemoveMode = -1;
    private Rect mTempRect = new Rect();
    private Bitmap mDragBitmap;
    private final int mTouchSlop;
    private int mItemHeightNormal;
    private int mItemHeightExpanded;

    public DndListView(Context context, AttributeSet attrs) {
        super(context, attrs);
//        SharedPreferences pref = context.getSharedPreferences("Music", 3);
//        mRemoveMode = pref.getInt("deletemode", -1);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mContext = context;
//        Resources res = getResources();
//        mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
//        mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mDragListener != null || mDropListener != null) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    int x = (int) ev.getX();
                    int y = (int) ev.getY();
                    int itemnum = pointToPosition(x, y);
                    if (itemnum == AdapterView.INVALID_POSITION) {
                        break;
                    }
                    ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
                    mDragPoint = y - item.getTop();
                    mCoordOffset = ((int)ev.getRawY()) - y;
                    View dragger = item.findViewById(R.id.dragicon);

//                    item.setBackgroundColor(Color.RED);

                    Rect r = mTempRect;
                    dragger.getDrawingRect(r);
                    // The dragger icon itself is quite small, so pretend the touch area is bigger
                    if (x < r.right * 2) {
                        item.setDrawingCacheEnabled(true);
                        // Create a copy of the drawing cache so that it does not get recycled
                        // by the framework when the list tries to clean up memory
                        Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
                        startDragging(bitmap, y);
                        mDragPos = itemnum;
                        mFirstDragPos = mDragPos;
                        mHeight = getHeight();
                        int touchSlop = mTouchSlop;
                        mUpperBound = Math.min(y - touchSlop, mHeight / 3);
                        mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
                        return false;
                    }
                    mDragView = null;
                    break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    /*
     * pointToPosition() doesn't consider invisible views, but we
     * need to, so implement a slightly different version.
     */
    private int myPointToPosition(int x, int y) {
        Rect frame = mTempRect;
        final int count = getChildCount();
        for (int i = count - 1; i >= 0; i--) {
            final View child = getChildAt(i);
            child.getHitRect(frame);
            if (frame.contains(x, y)) {
                return getFirstVisiblePosition() + i;
            }
        }
        return INVALID_POSITION;
    }

    private int getItemForPosition(int y) {
        int adjustedy = y - mDragPoint - 32;
        int pos = myPointToPosition(0, adjustedy);
        if (pos >= 0) {
            if (pos <= mFirstDragPos) {
                pos += 1;
            }
        } else if (adjustedy < 0) {
            pos = 0;
        }
        return pos;
    }

    private void adjustScrollBounds(int y) {
        if (y >= mHeight / 3) {
            mUpperBound = mHeight / 3;
        }
        if (y <= mHeight * 2 / 3) {
            mLowerBound = mHeight * 2 / 3;
        }
    }

    /*
     * Restore size and visibility for all listitems
     */
    private void unExpandViews(boolean deletion) {
        for (int i = 0;; i++) {
            View v = getChildAt(i);
            if (v == null) {
                if (deletion) {
                    // HACK force update of mItemCount
                    int position = getFirstVisiblePosition();
                    int y = getChildAt(0).getTop();
                    setAdapter(getAdapter());
                    setSelectionFromTop(position, y);
                    // end hack
                }
                layoutChildren(); // force children to be recreated where needed
                v = getChildAt(i);
                if (v == null) {
                    break;
                }
            }
            ViewGroup.LayoutParams params = v.getLayoutParams();
            params.height = mItemHeightNormal;
            v.setLayoutParams(params);
            v.setVisibility(View.VISIBLE);
        }
    }

    /* Adjust visibility and size to make it appear as though
     * an item is being dragged around and other items are making
     * room for it:
     * If dropping the item would result in it still being in the
     * same place, then make the dragged listitem's size normal,
     * but make the item invisible.
     * Otherwise, if the dragged listitem is still on screen, make
     * it as small as possible and expand the item below the insert
     * point.
     * If the dragged item is not on screen, only expand the item
     * below the current insertpoint.
     */
    private void doExpansion() {
        int childnum = mDragPos - getFirstVisiblePosition();
        if (mDragPos > mFirstDragPos) {
            childnum++;
        }

        View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());

        for (int i = 0;; i++) {
            View vv = getChildAt(i);
            if (vv == null) {
                break;
            }
            int height = mItemHeightNormal;
            int visibility = View.VISIBLE;
            if (vv.equals(first)) {
                // processing the item that is being dragged
                if (mDragPos == mFirstDragPos) {
                    // hovering over the original location
                    visibility = View.INVISIBLE;
                } else {
                    // not hovering over it
                    height = 1;
                }
            } else if (i == childnum) {
                if (mDragPos < getCount() - 1) {
                    height = mItemHeightExpanded;
                }
            }
            ViewGroup.LayoutParams params = vv.getLayoutParams();
            params.height = height;
            vv.setLayoutParams(params);
            vv.setVisibility(visibility);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mGestureDetector != null) {
            mGestureDetector.onTouchEvent(ev);
        }
        if ((mDragListener != null || mDropListener != null) && mDragView != null) {
            int action = ev.getAction(); 
            switch (action) {
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    Rect r = mTempRect;
                    mDragView.getDrawingRect(r);
                    stopDragging();
//                    if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
//                        if (mRemoveListener != null) {
//                            mRemoveListener.remove(mFirstDragPos);
//                        }
//                        unExpandViews(true);
//                    } else {
                    if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
                        mDropListener.drop(mFirstDragPos, mDragPos);
                    }
                    unExpandViews(false);
//                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    int x = (int) ev.getX();
                    int y = (int) ev.getY();
                    dragView(x, y);
                    int itemnum = getItemForPosition(y);
                    if (itemnum >= 0) {
                        if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
                            if (mDragListener != null) {
                                mDragListener.drag(mDragPos, itemnum);
                            }
                            mDragPos = itemnum;
                            doExpansion();
                        }
                        int speed = 0;
                        adjustScrollBounds(y);
                        if (y > mLowerBound) {
                            // scroll the list up a bit
                            speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
                        } else if (y < mUpperBound) {
                            // scroll the list down a bit
                            speed = y < mUpperBound / 2 ? -16 : -4;
                        }
                        if (speed != 0) {
                            int ref = pointToPosition(0, mHeight / 2);
                            if (ref == AdapterView.INVALID_POSITION) {
                                //we hit a divider or an invisible view, check somewhere else
                                ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);
                            }
                            View v = getChildAt(ref - getFirstVisiblePosition());
                            if (v!= null) {
                                int pos = v.getTop();
                                setSelectionFromTop(ref, pos - speed);
                            }
                        }
                    }
                    break;
            }
            return true;
        }
        return super.onTouchEvent(ev);
    }

    private void startDragging(Bitmap bm, int y) {
        stopDragging();

        mWindowParams = new WindowManager.LayoutParams();
        mWindowParams.gravity = Gravity.TOP;
        mWindowParams.x = 0;
        mWindowParams.y = y - mDragPoint + mCoordOffset;
//
        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 = mContext.getResources().getColor(R.color.dragndrop_background);
//        int backGroundColor = Color.parseColor("#e0103010");
        v.setBackgroundColor(backGroundColor);

        v.setImageBitmap(bm);
        mDragBitmap = bm;

        mWindowManager = (WindowManager)mContext.getSystemService("window");
        mWindowManager.addView(v, mWindowParams);
        mDragView = v;
    }

    private void dragView(int x, int y) {
//        if (mRemoveMode == SLIDE) {
//            float alpha = 1.0f;
//            int width = mDragView.getWidth();
//            if (x > width / 2) {
//                alpha = ((float)(width - x)) / (width / 2);
//            }
//            mWindowParams.alpha = alpha;
//        }
        mWindowParams.y = y - mDragPoint + mCoordOffset;
        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 setDragListener(DragListener l) {
        mDragListener = l;
    }

    public void setDropListener(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);
    }
}
위 코드중 정상적인 컴파일을 위해서 살펴보아야 할 부분이 두군데 있습니다.
위 코드에 다음과 같은 부분이 있습니다.
View dragger = item.findViewById(R.id.dragicon);
R.id.dragicon 이 바로 드래그를 할 대상이 되는 View가 됩니다.
또 다음과 같은 코드가 있습니다.
int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);
이 부분은 드래그 앤 드롭시 백그라운드 색상을 지정해 주는 부분입니다.
위 색상 지정을 위해 strings.xml파일에 다음과 같은 항목을 추가해 주어야 합니다.
<color name="dragndrop_background">#e0103010</color>
이제 드래그 앤 드롭을 구현하기 위해서 ListActivity는 어떻게 구현해야 하는지 알아보겠습니다.
아래와 같이 DragListener와 DropListener를 구현하도록 ListActivity를 만듭니다.
public class MainActivity extends ListActivity implements DragListener, DropListener {
onCreate메써드에서 드래그 앤 드롭을 사용한다는 정보를 입력합니다.
listView = (DndListView) findViewById(android.R.id.list);
listView.setDragListener(this);
listView.setDropListener(this);
그리고 다음과 같은 메써드를 구현합니다.
public void drag(int from, int to) {
  // 드래그 이벤트가 발생시 구현해야 할 것들을 기술한다.
}

public void drop(int fr, int to) {
  // 드롭 이벤트 발생시 구현해야 할 것들을 기술한다.
}
Activity의 레이아웃 파일은 다음과 같이 작성해야 합니다.
<org.pyframe.tools.view.DndListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:drawSelectorOnTop="false"
        android:fastScrollEnabled="true"
        android:cacheColorHint="#00000000"
        android:layout_weight="1.0"
         />
cacheColorHint값을 주어야 드래그 앤 드롭시 선택된 아이템의 백그라운드 색상이 표시됩니다.

댓글

이 블로그의 인기 게시물

[Java] Http File Download 이어받기

Http 서버로부터 다운로드 받는 파일을 이어받기 위해서는 Http Header에 아래 두가지 정보를 추가해 주면 된다. URLConnection conn = url.openConnection(); conn.setRequestProperty("Accept-Ranges", "bytes"); conn.setRequestProperty("Range", "bytes=" + mOffset + "-"); 그러면 서버에서는 해당 Offset으로부터 File을 다운로드 시켜준다. 클라이언트가 요청헤더에 Range 필드를 포함 시켜서 보내면, 서버는 그 정보를 가지고 어디서 부터 파일을 보낼지 판단을 합니다. 하지만 클라이언트가(브라우저) Range 필드를 포함 시켜야 할지를 판단하는 기준은 최초 다운로드 요청시 서버의 응답헤더에 따라 다음 요청헤더에 Range 헤더를 생성할지 않할지 판단하게 됩니다. 그걸 당락짓는 응답 헤더 필드는 다음과 같습니다. Accept-Ranges , ETag, Last-Modified 반드시 위 필드를 응답 헤더에 같이 보내줘야 클라이언트는 다음 요청시 Range헤더를 포함시켜 보내게 됩니다. 참고로 말씀 드리면 위 필드를 포함 시켜서 보내더라도 value는 반드시 " " 로 묶어서 보내야 합니다. 안그러면 브라우저는 죽어도 Range 필드를 생성시키지 않습니다. HTTP 1.1 스펙은 따옴표를 강제적으로 해줘라 이런 내용 없습니다. 자바기준 40byte의 파일이라치면 클리이언트 요청을 두번으로 나누었다치면 이케 connection.setRequestProperty("Range", "bytes=0-20"); connection.setRequestProperty("Range", "bytes=20-40"); 단 co...

java 특정 디렉토리에 있는 파일 목록을 읽어내기, 정렬해서 가져오기

폴더 리스트 가져오기 String path="C:\"; File dirFile=new File(path); File []fileList=dirFile.listFiles(); for(File tempFile : fileList) {   if(tempFile.isFile()) {     String tempPath=tempFile.getParent();     String tempFileName=tempFile.getName();     System.out.println("Path="+tempPath);     System.out.println("FileName="+tempFileName);     /*** Do something withd tempPath and temp FileName ^^; ***/   } } 정렬해서 가져오기 import java.io.FileFilter; import java.io.IOException; import java.util.Arrays; import java.util.Date; import org.apache.commons.io.comparator.LastModifiedFileComparator; import org.apache.commons.io.filefilter.FileFileFilter; public class LastModifiedFileComparatorTest { public static void main(String[] args) throws IOException { File directory = new File("."); // get just files, not directories File[] files = directory.listFiles((FileFilter) FileFileFilter.FILE); System.out.println("Defaul...