天天看點

gridview拖拽排序

可自由設定不能拖拽的位置,選中的位置,及拖拽的鏡像樣式      
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import com.dealmoon.android.R;
import com.north.expressnews.home.TextViewAdapter;

public class DragGridViewItemLine extends GridView {
    /**
     * DragGridView的item長按響應的時間, 預設是1000毫秒,也可以自行設定
     */
    private long dragResponseMS = 1000;

    /**
     * 是否可以拖拽,預設不可以
     */
    private boolean isDrag = false;

    /**
     * 功能設定:是否能拖拽
     */
    private boolean dragEnable = true;

    /**
     * 設定不能拖動的位置
     * 忽略的位置,用于長按此類位置不進行移動
     */
    private int[] ignorePositions;

    private int mDownX;
    private int mDownY;
    private int moveX;
    private int moveY;
    /**
     * 正在拖拽的position
     */
    private int mDragPosition;

    /**
     * 剛開始拖拽的item對應的View
     */
    private View mStartDragItemView = null;

    /**
     * 用于拖拽的鏡像,這裡直接用一個ImageView
     */
    private ImageView mDragImageView;

    /**
     * 震動器
     */
    private Vibrator mVibrator;

    private WindowManager mWindowManager;
    /**
     * item鏡像的布局參數
     */
    private WindowManager.LayoutParams mWindowLayoutParams;

    /**
     * 我們拖拽的item對應的Bitmap
     */
    private Bitmap mDragBitmap;

    /**
     * 按下的點到所在item的上邊緣的距離
     */
    private int mPoint2ItemTop;

    /**
     * 按下的點到所在item的左邊緣的距離
     */
    private int mPoint2ItemLeft;

    /**
     * DragGridView距離螢幕頂部的偏移量
     */
    private int mOffset2Top;

    /**
     * DragGridView距離螢幕左邊的偏移量
     */
    private int mOffset2Left;

    /**
     * 狀态欄的高度
     */
    private int mStatusHeight;

    /**
     * DragGridView自動向下滾動的邊界值
     */
    private int mDownScrollBorder;

    /**
     * DragGridView自動向上滾動的邊界值
     */
    private int mUpScrollBorder;

    /**
     * DragGridView自動滾動的速度
     */
    private static final int speed = 20;

    private boolean isNeedItemBorder = true;

    private int mDragItemBackground = 0;

    public void setNeedItemBorder(boolean needItemBorder) {
        isNeedItemBorder = needItemBorder;
    }

    public void setDragItemBackground(int bg) {
        mDragItemBackground = bg;
    }

    /**
     * item發生變化回調的接口
     */
    private OnChanageListener onChanageListener;

    public DragGridViewItemLine(Context context) {
        this(context, null);
    }

    public DragGridViewItemLine(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragGridViewItemLine(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mVibrator = (Vibrator) context
                .getSystemService(Context.VIBRATOR_SERVICE);
        mWindowManager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        mStatusHeight = getStatusHeight(context); // 擷取狀态欄的高度

    }

    public void setDragEnable(boolean enable) {
        dragEnable = enable;
    }

    /**
     * 設定不能拖動的位置
     * 忽略的位置,用于長按此類位置不進行移動
     */
    public void setIgnoreDragPosition(@NonNull int[] ignorePositions) {
        this.ignorePositions = ignorePositions;
    }


    private boolean checkIsIgnoreDragPosition(int dragPosition) {
        if (ignorePositions != null) {
            for (int i = 0, j = ignorePositions.length; i < j; i++) {
                if (dragPosition == ignorePositions[i]) {
                    return true;
                }
            }
        }
        return false;
    }
    

    private Handler mHandler = new Handler();

    // 用來處理是否為長按的Runnable
    private Runnable mLongClickRunnable = new Runnable() {

        @Override
        public void run() {
            isDrag = true; // 設定可以拖拽
            mVibrator.vibrate(50); // 震動一下

            ColorStateList colorStateList = setTextColor(mStartDragItemView, getContext().getResources().getColor(R.color.white));
            // 開啟mDragItemView繪圖緩存
            mStartDragItemView.setDrawingCacheEnabled(true);
            // 緩存背景色
            if (mDragItemBackground != 0) {
                mStartDragItemView.setBackgroundResource(mDragItemBackground);
                mStartDragItemView.setDrawingCacheBackgroundColor(Color.TRANSPARENT);
            } else {
                mStartDragItemView.setDrawingCacheBackgroundColor(getContext().getResources().getColor(R.color.dm_main));
            }
            // 擷取mDragItemView在緩存中的Bitmap對象
            mDragBitmap = Bitmap.createBitmap(mStartDragItemView
                    .getDrawingCache());
            // 這一步很關鍵,釋放繪圖緩存,避免出現重複的鏡像
            mStartDragItemView.destroyDrawingCache();

            setTextColor(mStartDragItemView, colorStateList);
            
            mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏該item

            // 根據我們按下的點顯示item鏡像
            createDragImage(mDragBitmap, mDownX, mDownY);
        }
    };


    private ColorStateList setTextColor(View parentView, int textColor) {
        ColorStateList colorStateList = null;
        System.out.println("setTextColor "+parentView.toString());
        if (parentView instanceof TextView) {
            colorStateList = ((TextView) parentView).getTextColors();

            ((TextView) parentView).setTextColor(textColor);
            parentView.invalidate();
        }
//        else if (parentView instanceof AbsListView) {
//            for (int i = 0, j = ((AbsListView) parentView).getChildCount(); i < j; i++) {
//                if (((AbsListView) parentView).getChildAt(i) instanceof TextView) {
//                    TextView tx = (TextView) ((AbsListView) parentView).getChildAt(i);
//
//                    colorStateList = tx.getTextColors();
//
//                    tx.setTextColor(textColor);
//                    tx.invalidate();
//                }
//
//            }
//        } 
        else if (parentView instanceof ViewGroup) {
            for (int i = 0, j = ((ViewGroup) parentView).getChildCount(); i < j; i++) {
                if (((ViewGroup) parentView).getChildAt(i) instanceof TextView) {
                    TextView tx = (TextView) ((ViewGroup) parentView).getChildAt(i);

                    colorStateList = tx.getTextColors();

                    tx.setTextColor(textColor);
                    tx.invalidate();
                }

            }
        }
        return colorStateList;
    }

    private void setTextColor(View parentView, ColorStateList colorStateList) {
        if (parentView instanceof TextView) {
            ((TextView) parentView).setTextColor(colorStateList);
            parentView.invalidate();
        }
//        else if (parentView instanceof AbsListView) {
//            for (int i = 0, j = ((AbsListView) parentView).getChildCount(); i < j; i++) {
//                if (((AbsListView) parentView).getChildAt(i) instanceof TextView) {
//                    TextView tx = (TextView) ((AbsListView) parentView).getChildAt(i);
//                    tx.setTextColor(colorStateList);
//                    tx.invalidate();
//                }
//
//            }
//        }
        else if (parentView instanceof ViewGroup) {
            for (int i = 0, j = ((ViewGroup) parentView).getChildCount(); i < j; i++) {
                if (((ViewGroup) parentView).getChildAt(i) instanceof TextView) {
                    TextView tx = (TextView) ((ViewGroup) parentView).getChildAt(i);
                    tx.setTextColor(colorStateList);
                    tx.invalidate();
                }

            }
        }
    }
    
    
    
    
    /**
     * 設定回調接口
     *
     * @param onChanageListener
     */
    public void setOnChangeListener(OnChanageListener onChanageListener) {
        this.onChanageListener = onChanageListener;
    }

    /**
     * 設定響應拖拽的毫秒數,預設是1000毫秒
     *
     * @param dragResponseMS
     */
    public void setDragResponseMS(long dragResponseMS) {
        this.dragResponseMS = dragResponseMS;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (isNeedItemBorder) {
            View localView1 = getChildAt(0);
            int column = getWidth() / localView1.getWidth();
            int childCount = getChildCount();
            Paint localPaint;
            localPaint = new Paint();
            localPaint.setStyle(Paint.Style.STROKE);
            localPaint.setColor(getContext().getResources().getColor(R.color.dm_line));
            for (int i = 0; i < childCount; i++) {
                View cellView = getChildAt(i);
                if ((i + 1) % column == 0) {
                    canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
                } else if ((i + 1) > (childCount - (childCount % column))) {
                    canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
                } else {
                    canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
                    canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
                }
            }
            if (childCount % column != 0) {
                for (int j = 0; j < (column - childCount % column); j++) {
                    View lastView = getChildAt(childCount - 1);
                    canvas.drawLine(lastView.getRight() + lastView.getWidth() * j, lastView.getTop(), lastView.getRight() + lastView.getWidth() * j, lastView.getBottom(), localPaint);
                }
            }
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        if (!dragEnable) {
            return super.dispatchTouchEvent(ev);
        }

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mDownX = (int) ev.getX();
                mDownY = (int) ev.getY();
                mDragPosition = pointToPosition(mDownX, mDownY);
                Log.i("+++++++itempos++++++", mDragPosition + "----"
                        + getAdapter().getCount());

                if (mDragPosition == 0 || mDragPosition == AdapterView.INVALID_POSITION) {

                    return super.dispatchTouchEvent(ev);
                }

                if (checkIsIgnoreDragPosition(mDragPosition)) {
                    return super.dispatchTouchEvent(ev);
                }
                

                if (getParent() != null) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                
                
                if (mDragPosition < getAdapter().getCount()) {
                    // 使用Handler延遲dragResponseMS執行mLongClickRunnable

                    mHandler.postDelayed(mLongClickRunnable, dragResponseMS);

                }

                // 根據position擷取該item所對應的View
                mStartDragItemView = getChildAt(mDragPosition
                        - getFirstVisiblePosition());
              
                // 下面這幾個距離大家可以參考我的部落格上面的圖來了解下
                mPoint2ItemTop = mDownY - mStartDragItemView.getTop();
                mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft();

                mOffset2Top = (int) (ev.getRawY() - mDownY);
                mOffset2Left = (int) (ev.getRawX() - mDownX);

                // 擷取DragGridView自動向上滾動的偏移量,小于這個值,DragGridView向下滾動
                mDownScrollBorder = getHeight() / 4;
                // 擷取DragGridView自動向下滾動的偏移量,大于這個值,DragGridView向上滾動
                mUpScrollBorder = getHeight() * 3 / 4;

//                // 開啟mDragItemView繪圖緩存
//                mStartDragItemView.setDrawingCacheEnabled(true);
//                // 緩存背景色
//                mStartDragItemView.setDrawingCacheBackgroundColor(Color.parseColor("#FFFFFF00"));
//                // 擷取mDragItemView在緩存中的Bitmap對象
//                mDragBitmap = Bitmap.createBitmap(mStartDragItemView
//                        .getDrawingCache());
//                // 這一步很關鍵,釋放繪圖緩存,避免出現重複的鏡像
//                mStartDragItemView.destroyDrawingCache();

                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) ev.getX();
                int moveY = (int) ev.getY();

                if (getParent() != null) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                
                // 如果我們在按下的item上面移動,隻要不超過item的邊界我們就不移除mRunnable
                if (!isTouchInItem(mStartDragItemView, moveX, moveY)) {
                    mHandler.removeCallbacks(mLongClickRunnable);
                }
                break;
            case MotionEvent.ACTION_UP:
                int moveXup = (int) ev.getX();
                int moveYup = (int) ev.getY();
                // if (Math.abs(moveXup - mDownX) < 5 && Math.abs(moveYup - mDownY)
                // < 5) {
                // isDrag = true;
                // } else {
                // isDrag = false;
                // }
                
                if (getParent() != null) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                
                mHandler.removeCallbacks(mLongClickRunnable);
                mHandler.removeCallbacks(mScrollRunnable);
                break;
        }
        super.dispatchTouchEvent(ev);

        // Log.i("+++++++ACTION_UP++++++", "+++++++ACTION_UP++++++" +longclick
        // );

        return super.dispatchTouchEvent(ev);
    }

    /**
     * 是否點選在GridView的item上面
     *
     * @param dragView
     * @param x
     * @param y
     * @return
     */
    private boolean isTouchInItem(View dragView, int x, int y) {
        if (dragView == null) {
            return false;
        }
        int leftOffset = dragView.getLeft();
        int topOffset = dragView.getTop();
        if (x < leftOffset || x > leftOffset + dragView.getWidth()) {
            return false;
        }

        if (y < topOffset || y > topOffset + dragView.getHeight()) {
            return false;
        }

        return true;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!dragEnable) {
            return super.onTouchEvent(ev);
        }

        if (isDrag && mDragImageView != null) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    moveX = (int) ev.getX();
                    moveY = (int) ev.getY();
                    // 拖動item
                    onDragItem(moveX, moveY);
                    break;
                case MotionEvent.ACTION_UP:
                    onStopDrag();
                    isDrag = false;
                    break;
            }
            return true;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 建立拖動的鏡像
     *
     * @param bitmap
     * @param downX  按下的點相對父控件的X坐标
     * @param downY  按下的點相對父控件的X坐标
     */
    private void createDragImage(Bitmap bitmap, int downX, int downY) {
        mWindowLayoutParams = new WindowManager.LayoutParams();
//    mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 圖檔之外的其他地方透明
        mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
        mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;
        mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top
                - mStatusHeight;
//    mWindowLayoutParams.alpha = 0.55f; // 透明度
        mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        mDragImageView = new ImageView(getContext());
        mDragImageView.setImageBitmap(bitmap);
        mWindowManager.addView(mDragImageView, mWindowLayoutParams);
    }

    /**
     * 從界面上面移動拖動鏡像
     */
    private void removeDragImage() {
        if (mDragImageView != null) {
            mWindowManager.removeView(mDragImageView);
            mDragImageView = null;
        }
    }

    /**
     * 拖動item,在裡面實作了item鏡像的位置更新,item的互相交換以及GridView的自行滾動
     *
     * @param moveX
     * @param moveY
     */
    private void onDragItem(int moveX, int moveY) {
            mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left;
            mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top
                    - mStatusHeight;
            mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); // 更新鏡像的位置
            onSwapItem(moveX, moveY);

            // GridView自動滾動
            mHandler.post(mScrollRunnable);
//        }
      
    }

    /**
     * 當moveY的值大于向上滾動的邊界值,觸發GridView自動向上滾動 當moveY的值小于向下滾動的邊界值,觸犯GridView自動向下滾動
     * 否則不進行滾動
     */
    private Runnable mScrollRunnable = new Runnable() {

        @Override
        public void run() {
            int scrollY;
            if (getFirstVisiblePosition() == 0
                    || getLastVisiblePosition() == getCount() - 1) {
                mHandler.removeCallbacks(mScrollRunnable);
            }
            if (moveY > mUpScrollBorder) {
                scrollY = speed;
                mHandler.postDelayed(mScrollRunnable, 25);
            } else if (moveY < mDownScrollBorder) {
                scrollY = -speed;
                mHandler.postDelayed(mScrollRunnable, 25);
            } else {
                scrollY = 0;
                mHandler.removeCallbacks(mScrollRunnable);
            }

            // 當我們的手指到達GridView向上或者向下滾動的偏移量的時候,可能我們手指沒有移動,但是DragGridView在自動的滾動
            // 是以我們在這裡調用下onSwapItem()方法來交換item
            onSwapItem(moveX, moveY);

            smoothScrollBy(scrollY, 10);
        }
    };

    /**
     * 交換item,并且控制item之間的顯示與隐藏效果
     *
     * @param moveX
     * @param moveY
     */
    private void onSwapItem(int moveX, int moveY) {
        // 擷取我們手指移動到的那個item的position
        int tempPosition = pointToPosition(moveX, moveY);
        if(tempPosition == 0){
            return;
        }
        else 
        if (tempPosition < getAdapter().getCount()) {

            // 假如tempPosition 改變了并且tempPosition不等于-1,則進行交換
            if (tempPosition != mDragPosition
                    && tempPosition != AdapterView.INVALID_POSITION) {
                if (onChanageListener != null) {
                    onChanageListener.onChange(mDragPosition, tempPosition);
                }

                getChildAt(tempPosition - getFirstVisiblePosition())
                        .setVisibility(View.INVISIBLE);// 拖動到了新的item,新的item隐藏掉
                getChildAt(mDragPosition - getFirstVisiblePosition())
                        .setVisibility(View.VISIBLE);// 之前的item顯示出來

                mDragPosition = tempPosition;
            }

        }
    }

    /**
     * 停止拖拽我們将之前隐藏的item顯示出來,并将鏡像移除
     */
    private void onStopDrag() {
        View view = getChildAt(mDragPosition - getFirstVisiblePosition());
        if (view != null) {
            view.setVisibility(View.VISIBLE);
        }

        if (this.getAdapter() instanceof OnHideItemListener) {
            ((OnHideItemListener) getAdapter()).onItemHide(-1);
        } 
        
        removeDragImage();
    }

    /**
     * 擷取狀态欄的高度
     *
     * @param context
     * @return
     */
    private static int getStatusHeight(Context context) {
        int statusHeight = 0;
        Rect localRect = new Rect();
        ((Activity) context).getWindow().getDecorView()
                .getWindowVisibleDisplayFrame(localRect);
        statusHeight = localRect.top;
        if (0 == statusHeight) {
            Class<?> localClass;
            try {
                localClass = Class.forName("com.android.internal.R$dimen");
                Object localObject = localClass.newInstance();
                int i5 = Integer.parseInt(localClass
                        .getField("status_bar_height").get(localObject)
                        .toString());
                statusHeight = context.getResources().getDimensionPixelSize(i5);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusHeight;
    }

    /**
     * @author xiaanming
     */
    public interface OnChanageListener {

        /**
         * 當item交換位置的時候回調的方法,我們隻需要在該方法中實作資料的交換即可
         *
         * @param form 開始的position
         * @param to   拖拽到的position
         */
        public void onChange(int form, int to);
    }


    /**
     * hide item
     * <p>
     * Created by hl on 2017/4/10.
     */
    public interface OnHideItemListener {
        public void onItemHide(int position);
    }
    
    
}