andorid可拖拽的gridview

GridView拖拽排序算是个比较不错的效果,有不少程序实现了这个效果,闲来无事,我也山寨一把。

效果

运行效果

思路

1.当item被长按的时候,在屏幕里适当的位置添加一个item的镜像,所谓镜像就是一个ImageView,这个ImageView的内容就是指定的View生成的图像。
2.当手指滑动的时候,移动镜像到适当的位置,同时判断手指是否滑动到了其他的item上。如果手指移动到其他item上,那么相关的item做Translate动画、调用适配器里的方法调换数据的顺序、刷新界面。
3.手指抬起的时候,去除dragView,刷新界面。

Notice:由于上述第二步对相关的item进行了移动,并且没有移动回来,所以需要刷新界面生成新的item,这样Adapter里的getView不能使用contentView的复用。如果想实现contentView的复用也很简单,把上述第二步里相关的item做Translate动画改为添加一层,生成相关item的镜像,用镜像实现动画效果就ok。

我把GridView的子类BaseDragGrid定义成了抽象类,这样可以自定义镜像的实现。

代码

适配器代码

/**
 * Created by qingtian on 2015/12/20.
 */
public interface IDragGridAdapter {
    /**
     * 前面的某几个item不可以改变位置
     * @return
     */
    public int getFixedItemSize();
    /**
     * 前列的数量
     * @return
     */
    public int getColumnCount();

    /**
     * 当gridview里出现拖动后,调用这个方法,改变数据的排序,刷新界面
     * @param dragPostion  起始拖拽的位置
     * @param dropPostion  结束拖拽的位置
     */
    public void exchange(int dragPostion, int dropPostion);

    /**
     * 设置当前鼠标拖动到那个item上了
     * @param dropItemPosition  鼠标所在的item的序号
     */
    public void setDropItemPosition(int dropItemPosition);

    /**
     * 设置鼠标所在的item是否显示
     * @param show
     */
    public void setShowDropItem(boolean show);
}

View的代码

/**
 * 可以拖动的gridview
 * 子类实现抽象方法,实现拖动的item不同外观
 *
 * Created by qingtian on 2015/12/22.
 *
 * @blog http://blog.csdn.net/bingospunky
 */
public abstract class BaseDragGrid extends GridView {

    private static final String TAG = "qingtian";

    /** 设置刚开始的某几个item不可以改变位置 */
    public int mFixedItemSize = 0;

    /** 点击时候的X位置 */
    public int mDownX;
    /** 点击时候的Y位置 */
    public int mDownY;
    /** 点击时候的相对于屏幕的X位置 */
    public int mDownRawX;
    /** 点击时候的相对于屏幕的Y位置 */
    public int mDownRawY;

    /** 长按时,触点在Item View里面的X方向上的偏移量,数值大小(正数) */
    private int mXOffsetBetweenDownPointAndTheItemLeftBoundary;

    /** 长按时,触点在Item View里面的Y方向上的偏移量,数值大小(正数) */
    private int mYOffsetBetweenDownPointAndTheItemTopBoundary;

    /** 开始拖动的ITEM的Position */
    private int mStartPosition;

    /**  Up后对应的ITEM的Position */
    private int mDropPosition;

    /** item height */
    private int mItemHeight;

    /** item width */
    private int mItemWidth;

    /** 拖动的时候对应ITEM的VIEW */
    private View mDragImageView = null;

    /** WindowManager */
    private WindowManager mWindowManager = null;

    /** */
    private WindowManager.LayoutParams mWindowParams = null;

    /** 是否在移动 */
    private boolean isMoving = false;

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

    /** 每个ITEM之间的水平间距 */
    private int mHorizontalSpacing = 2;

    /** 每个ITEM之间的竖直间距 */
    private int mVerticalSpacing = 2;

    /** 一行的ITEM数量 */
    private int mNumColumns = 4;

    /** 移动时候最后个动画的ID */
    private String mLastAnimationID;

    private IDragGridAdapter mDragGridAdapter;

    public BaseDragGrid(Context context) {
        super(context);
        init(context);
    }

    public BaseDragGrid(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public BaseDragGrid(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public void init(Context context) {
        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
        setOnItemClickListener();
    }

    @Override
    public void setHorizontalSpacing(int horizontalSpacing) {
        super.setHorizontalSpacing(horizontalSpacing);
        this.mHorizontalSpacing = horizontalSpacing;
        Log.e(TAG, "setHorizontalSpacing:" + horizontalSpacing);
    }

    @Override
    public void setVerticalSpacing(int verticalSpacing) {
        super.setVerticalSpacing(verticalSpacing);
        this.mVerticalSpacing = verticalSpacing;
        Log.e(TAG, "verticalSpacing:" + verticalSpacing);
    }

    @Override
    public void setNumColumns(int numColumns) {
        super.setNumColumns(numColumns);
        this.mNumColumns = numColumns;
        Log.e(TAG, "setNumColumns:" + mNumColumns);
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);
        if (adapter instanceof IDragGridAdapter) {
            mDragGridAdapter = (IDragGridAdapter) adapter;
            mNumColumns = mDragGridAdapter.getColumnCount();
        } else {
            Toast.makeText(getContext(), "适配器异常IDragGridAdapter", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 长按点击监听
     */
    public void setOnItemClickListener() {
        setOnItemLongClickListener(new OnItemLongClickListener() {

            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

                mStartPosition = position; // 第一次点击的postion
                if (mStartPosition >= mFixedItemSize) {
                    ViewGroup dragViewGroup = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());

                    mItemHeight = dragViewGroup.getHeight();
                    mItemWidth = dragViewGroup.getWidth();

                    // 如果特殊的这个不等于拖动的那个,并且不等于-1
                    if (mStartPosition != AdapterView.INVALID_POSITION) {
                        // 计算触电相对于item的偏移量
                        mXOffsetBetweenDownPointAndTheItemLeftBoundary = mDownX - dragViewGroup.getLeft();
                        mYOffsetBetweenDownPointAndTheItemTopBoundary = mDownY - dragViewGroup.getTop();

                        mVibrator.vibrate(50); // 设置震动时间

                        // 添加随手指移动的item图像的ImageView
                        Bitmap dragBitmap = generateDragBitmap(dragViewGroup);

                        startDrag(dragBitmap, mDownRawX, mDownRawY);

                        // 隐藏gridview里拖动的item
                        mDragGridAdapter.setDropItemPosition(mStartPosition);
                        mDragGridAdapter.setShowDropItem(false);

                        isMoving = false;
                        requestDisallowInterceptTouchEvent(true);
                        return true;
                    }
                }
                return false;

            }
        });
    }

    /**
     * 抽象方法,子类实现,通过当前的view得到拖拽的view的图像
     *
     * @param dragViewGroup
     * @return
     */
    public abstract Bitmap generateDragBitmap(ViewGroup dragViewGroup);

    /**
     * 在屏幕里添加一个ImageView,该ImageView的内容就是被长按的那个View
     *
     * @param dragBitmap
     * @param x
     * @param y
     */
    public void startDrag(Bitmap dragBitmap, int x, int y) {
        stopDrag();
        mWindowParams = new WindowManager.LayoutParams();// 获取WINDOW界面的
        // 这个必须加
        mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
        //得到preview左上角相对于屏幕的坐标
        mWindowParams.x = x - mXOffsetBetweenDownPointAndTheItemLeftBoundary;
        mWindowParams.y = y - mYOffsetBetweenDownPointAndTheItemTopBoundary;
        //设置拖拽item的宽和高
        mWindowParams.width = dragBitmap.getWidth(); // 放大dragScale倍,可以设置拖动后的倍数
        mWindowParams.height = dragBitmap.getHeight(); // 放大dragScale倍,可以设置拖动后的倍数
        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 iv = new ImageView(getContext());
        iv.setImageBitmap(dragBitmap);
        mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        mWindowManager.addView(iv, mWindowParams);
        mDragImageView = iv;
    }

    /**
     * 停止拖动 ,释放并初始化
     */
    private void stopDrag() {
        if (mDragImageView != null) {
            mWindowManager.removeView(mDragImageView);
            mDragImageView = null;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mDownX = (int) ev.getX();
            mDownY = (int) ev.getY();
            mDownRawX = (int) ev.getRawX();
            mDownRawY = (int) ev.getRawY();
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mDragImageView != null && mStartPosition != AdapterView.INVALID_POSITION) {

            // 移动时候的对应x,y位置
            int x = (int) ev.getX();
            int y = (int) ev.getY();

            switch (ev.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    onDrag((int) ev.getRawX(), (int) ev.getRawY());
                    if (!isMoving) {
                        onMove(x, y);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    stopDrag();
                    mDragGridAdapter.setShowDropItem(true);
                    requestDisallowInterceptTouchEvent(false);
                    break;
                default:
                    break;
            }
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 在拖动的时候,被拖动的item随着手指移动
     */
    private void onDrag(int rawx, int rawy) {
        if (mDragImageView != null) {
            mWindowParams.alpha = 0.6f;
            mWindowParams.x = rawx - mXOffsetBetweenDownPointAndTheItemLeftBoundary;
            mWindowParams.y = rawy - mYOffsetBetweenDownPointAndTheItemTopBoundary;
            mWindowManager.updateViewLayout(mDragImageView, mWindowParams);
        }
    }

    /**
     * 移动的时候触发
     */
    public void onMove(int x, int y) {
        // 拖动的VIEW下方的POSTION
        int dPosition = pointToPosition(x, y);
        // 判断下方的POSTION是否是最开始2个不能拖动的
        if (dPosition >= mFixedItemSize) {

            mDropPosition = dPosition;

            int movecount = mDropPosition - mStartPosition;

            if (movecount != 0) {
                //dragGroup设置为不可见
                ViewGroup dragGroup = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());
                dragGroup.setVisibility(View.INVISIBLE);

                float to_x;// 当前下方positon
                float to_y;// 当前下方右边positon
                //x_vlaue移动的距离百分比(相对于自己长度的百分比)
                float x_vlaue = ((float) mHorizontalSpacing / (float) mItemWidth) + 1.0f;
                //y_vlaue移动的距离百分比(相对于自己宽度的百分比)
                float y_vlaue = ((float) mVerticalSpacing / (float) mItemHeight) + 1.0f;

                for (int i = 0; i < Math.abs(movecount); i++) {
                    int holdPosition;
                    to_x = x_vlaue;
                    to_y = y_vlaue;
                    if (movecount > 0) {
                        // 拖动,使item序号变大
                        holdPosition = mStartPosition + i + 1;
                        if (mStartPosition / mNumColumns == holdPosition / mNumColumns) {
                            to_x = -x_vlaue;
                            to_y = 0;
                        } else if (holdPosition % 4 == 0) {
                            to_x = 3 * x_vlaue;
                            to_y = -y_vlaue;
                        } else {
                            to_x = -x_vlaue;
                            to_y = 0;
                        }
                    } else {
                        // 拖动,使item序号变小
                        holdPosition = mStartPosition - i - 1;
                        if (mStartPosition / mNumColumns == holdPosition / mNumColumns) {
                            to_x = x_vlaue;
                            to_y = 0;
                        } else if ((holdPosition + 1) % 4 == 0) {
                            to_x = -3 * x_vlaue;
                            to_y = y_vlaue;
                        } else {
                            to_x = x_vlaue;
                            to_y = 0;
                        }
                    }
                    ViewGroup moveViewGroup = (ViewGroup) getChildAt(holdPosition - getFirstVisiblePosition());
                    Animation moveAnimation = generateAnimation(to_x, to_y);
                    //如果是最后一个移动的,那么设置他的最后个动画ID为LastAnimationID
                    if (holdPosition == mDropPosition) {
                        mLastAnimationID = moveAnimation.toString();
                    }
                    moveAnimation.setAnimationListener(new AnimationListener() {

                        @Override
                        public void onAnimationStart(Animation animation) {
                            // TODO Auto-generated method stub
                            isMoving = true;
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {
                            // TODO Auto-generated method stub
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            // TODO Auto-generated method stub
                            // 如果为最后个动画结束,那执行下面的方法
                            if (animation.toString().equalsIgnoreCase(mLastAnimationID)) {
                                mDragGridAdapter.setDropItemPosition(mDropPosition);
                                mDragGridAdapter.exchange(mStartPosition, mDropPosition);
                                mStartPosition = mDropPosition;
                                isMoving = false;
                            }
                        }
                    });
                    moveViewGroup.startAnimation(moveAnimation);
                }
            }
        }
    }

    /**
     * 获取移动动画
     */
    public Animation generateAnimation(float toXValue, float toYValue) {
        TranslateAnimation mTranslateAnimation = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF, 0.0F,
                Animation.RELATIVE_TO_SELF, toXValue,
                Animation.RELATIVE_TO_SELF, 0.0F,
                Animation.RELATIVE_TO_SELF, toYValue);// 当前位置移动到指定位置
        mTranslateAnimation.setFillAfter(false);
        mTranslateAnimation.setDuration(300L);
        return mTranslateAnimation;
    }

}

源码下载

https://github.com/binbincivil/drag_adapterview

http://download.csdn.net/detail/u011647962/9375239

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值