可拖拽的GridView

来源:互联网 发布:数据科学 北大 编辑:程序博客网 时间:2024/05/22 00:12

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageView;

import com.ebensz.enote.notepad.R;
import com.ebensz.enote.notepad.browser.BrowserController;
import com.ebensz.enote.notepad.ui.adapter.BrowserGridAdapter;

public class DraggableGridView extends GridView {

    private static final int DRAG_MODE_DELAY = 500;
    private static final int DRAG_MODE_MSG = 10001;
    
    private static final int UP_SCROLL_BOUNCE = 150;// 拖动的时候,开始向上滚动的边界
    private static final int DOWN_SCROLL_BOUNCE = 850;// 拖动的时候,开始向下滚动的边界
    
    public DraggableGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        columns = context.getResources().getInteger(R.integer.grid_columns);
    }
    
    public void setBrowserController(BrowserController browserController) {
        this.browserController = browserController;
    }
    
    public boolean isDataChanged() {
        return isDataChanged;
    }
    
    public void cancelDrag() {
        isDataChanged = false;
        isDragMode = false;
    }
    
    public int getDropPosition() {
        return dropPosition;
    }
    
    public int getColumns() {
        return columns;
    }
    
    public void sendMsgDelayed() {
        handler.removeMessages(DRAG_MODE_MSG);
        if (!browserController.isStartDragEnable() || startPosition == AdapterView.INVALID_POSITION || getAdapter().getCount() < 2) {
            return;
        }
        
        handler.sendEmptyMessageDelayed(DRAG_MODE_MSG, DRAG_MODE_DELAY);
    }
    
    public View getChildViewAt(int index) {
        int position = getVisibleItemIndex(index);
        
        if (position < 0 || position > (getCount() - 1)) {
            return null;
        }
        
        return getChildAt(position);
    }
    
    @Override
    public boolean onHoverEvent(MotionEvent event) {
        handler.removeMessages(DRAG_MODE_MSG);
        
        if (isDragMode) {
            isDragMode = false;
            onDrop();
        }
        
        return super.onHoverEvent(event);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        final int rawY = (int) ev.getRawY();
        final int action = ev.getActionMasked();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            init(ev);
            if (null == itemView) {
                return super.onTouchEvent(ev);
            }
            
            if (isDragMode) {
                prepareDrag(itemView);
            }
            
            break;

        case MotionEvent.ACTION_MOVE:
            if (isDragMode) {
                onDrag(x, y, rawY);
                
                if (!isMoving && (rawY > UP_SCROLL_BOUNCE && rawY < DOWN_SCROLL_BOUNCE)) {
                    onMove(x, y);
                }
            }

            break;

        case MotionEvent.ACTION_UP:
            handler.removeMessages(DRAG_MODE_MSG);
            
            if (!isDataChanged && null != itemView) {
                itemView.setVisibility(View.VISIBLE);
            }
            
            if (isDragMode) {
                onDrop();
            }
            
            isDragMode = false;
            
            break;
        }

        return super.onTouchEvent(ev);
    }

    private void prepareDrag(View itemView) {
        itemView.destroyDrawingCache();
        itemView.setDrawingCacheEnabled(true);
        itemView.setDrawingCacheBackgroundColor(0x000000);

        Bitmap drawingCache = Bitmap.createBitmap(itemView
                .getDrawingCache(true));

        Bitmap substitute = Bitmap.createBitmap(drawingCache, 0, 0,
                drawingCache.getWidth(),
                drawingCache.getHeight());

        startDrag(substitute, downX, downY);

        setDropItemVisible(false);

        itemView.setVisibility(View.INVISIBLE);
    }

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == DRAG_MODE_MSG) {
                isDragMode = true;
                prepareDrag(itemView);
            }
        }
    };
    
    private void init(MotionEvent ev){
        downX = (int) ev.getX();
        downY = (int) ev.getY();
        
        startPosition = dragPosition = dropPosition = pointToPosition(downX, downY);
        
        if (dragPosition == AdapterView.INVALID_POSITION) {
            itemView = null;
            return;
        }
        
        itemView = getChildAt(dragPosition - getFirstVisiblePosition());

        // 得到当前点在item内部的偏移量 即相对于item左上角的坐标
        dragPointX = downX - itemView.getLeft();
        dragPointY = downY - itemView.getTop();

        dragOffsetX = (int) (ev.getRawX() - downX);
        dragOffsetY = (int) (ev.getRawY() - downY);
        
        isMoving = false;
    }
    
    private void startDrag(Bitmap bm, int x, int y) {
        stopDrag();

        windowParams = new WindowManager.LayoutParams();
        
        windowParams.gravity = Gravity.TOP | Gravity.LEFT;// 这个必须加
        // 得到preview左上角相对于屏幕的坐标
        windowParams.x = x - dragPointX + dragOffsetX - 10;
        windowParams.y = y - dragPointY + dragOffsetY - 10;
        // 设置宽和高
        windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

        windowParams.format = PixelFormat.TRANSLUCENT;
        windowParams.windowAnimations = 0;

        ImageView iv = new ImageView(getContext());
        iv.setImageBitmap(bm);
        windowManager = (WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE);// "window"
        windowManager.addView(iv, windowParams);
        dragImageView = iv;
    }
    
    private void enterDragMode() {
        browserController.exitSelectMode(false);
        browserController.enterDragMode();
    }
    
    private void onMove(int x, int y) {
        final int position = pointToPosition(x, y);
        
        if (position != AdapterView.INVALID_POSITION && position != dragPosition) {
            dropPosition = position;
        }

        dragPosition = startPosition;

        int moveNum = dropPosition - dragPosition;
        if (moveNum != 0) {

            enterDragMode();
            
            int moveNumAbs = Math.abs(moveNum);
            float offsetX, offsetY;

            for (int i = 0; i < moveNumAbs; i++) {

                int holdPosition;

                if (moveNum > 0) {
                    holdPosition = dragPosition + 1;
                    offsetX = (dragPosition / columns == holdPosition / columns) ? (-1) : (columns - 1);
                    offsetY = (dragPosition / columns == holdPosition / columns) ? 0 : (-1);
                }
                else {
                    holdPosition = dragPosition - 1;
                    offsetX = (dragPosition / columns == holdPosition / columns) ? 1 : (-(columns - 1));
                    offsetY = (dragPosition / columns == holdPosition / columns) ? 0 : 1;
                }

                final ViewGroup moveView =  (ViewGroup) getChildViewAt(holdPosition);
                Animation animation = getMoveAnimation(offsetX, offsetY);
                moveView.startAnimation(animation);

                dragPosition = holdPosition;
                
                if (dragPosition == dropPosition) {
                    lastAnimationID = animation.toString();
                }

                final BrowserGridAdapter adapter = (BrowserGridAdapter)getAdapter();
                animation.setAnimationListener(new AnimationListener() {
                    
                    @Override
                    public void onAnimationStart(Animation animation) {
                        isMoving = true;
                    }
                    
                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }
                    
                    @Override
                    public void onAnimationEnd(Animation animation) {
                        isMoved = true;
                        isDataChanged = true;
                        String animationId = animation.toString();

                        if (animationId.equalsIgnoreCase(lastAnimationID)) {
                            adapter.exchangePages(startPosition, dropPosition);
                            startPosition = dropPosition;
                            isMoving = false;
                        }
                        
                        moveView.clearAnimation();
                    }
                });
            }
        }
    }

    private int getVisibleItemIndex(int dragPosition) {
        int firstVisiblePosition = getFirstVisiblePosition();

        int index = Math.abs(dragPosition - firstVisiblePosition);
        int visibleCount = getLastVisiblePosition() - firstVisiblePosition;
        if (index > visibleCount) {
            index = visibleCount - 1;
        }

        return index;
    }
    
    private void onDrop() {
        stopDrag();
        
        final BrowserGridAdapter adapter = (BrowserGridAdapter) this.getAdapter();
        adapter.showDropItem(true);
        adapter.notifyDataSetChanged();
        
        if (isMoved) {
            browserController.setSelectedPosition(dropPosition);
            adapter.draggedChangeData(dropPosition);
            isMoved = false;
        }
    }

    private void onDrag(int x, int y,int rawY) {
        
        if (dragImageView != null) {
            windowParams.alpha = 0.6f;
            windowParams.x = x - dragPointX + dragOffsetX;
            windowParams.y = y - dragPointY + dragOffsetY;
            windowManager.updateViewLayout(dragImageView, windowParams);
        }
        
        // 滚动
        int scrollHeight = 0;
        if (rawY < UP_SCROLL_BOUNCE) {
            scrollHeight = -150;
        }
        else if (rawY > DOWN_SCROLL_BOUNCE) {
            scrollHeight = 150;
        }

        if (scrollHeight != 0) {
            smoothScrollBy(scrollHeight,0);
        }
    }

    private void stopDrag() {
        if (dragImageView != null) {
            windowManager.removeView(dragImageView);
            dragImageView = null;
        }
    }

    private Animation getMoveAnimation(float x, float y) {

        TranslateAnimation go = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF,
                0,
                Animation.RELATIVE_TO_SELF,
                x,
                Animation.RELATIVE_TO_SELF,
                0,
                Animation.RELATIVE_TO_SELF,
                y
        );

        go.setFillAfter(true);
        go.setDuration(150);

        return go;
    }

    private void setDropItemVisible(boolean isVisible) {
        final BrowserGridAdapter adapter = (BrowserGridAdapter) this.getAdapter();
        adapter.showDropItem(isVisible);
    }
    
    private BrowserController browserController;
    
    private View itemView;

    private boolean isDataChanged;
    private boolean isDragMode;
    
    private int columns;
    private int dragPosition;// 开始拖拽的位置
    private int dropPosition;// 结束拖拽的位置
    private int dragPointX; // 相对于item的x坐标
    private int dragPointY; // 相对于item的y坐标
    private int dragOffsetX;
    private int dragOffsetY;
    private ImageView dragImageView; // 拖动item的preview
    
    private WindowManager windowManager;
    private WindowManager.LayoutParams windowParams;
    
    private int startPosition;
    private int downX,downY;
    private String lastAnimationID;

    private boolean isMoved;
    private boolean isMoving;
}