Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)

来源:互联网 发布:windows 安装snmp协议 编辑:程序博客网 时间:2024/05/17 08:01


前言

上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动,详情请参考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和拖拽)

今天把相对比较复杂的gridView的拖拽也记录下,在开始之前我们事先要了解下Android的事件分发机制,网上这方面的资料也比较多。因为自定义控件大部分要用到事件分发机制的知识。

实现思路

要实现Item的拖拽,其实并不是真正要去拖拽GridView的Item,而是使用WindowManager添加一个Item的镜像来代替Item,进行移动拖拽的操作。

1、首先我们对touch事件进行分发,设置长按监听器。

2、获取我们长按的Item 隐藏它,然后用WindowManager添加一个Item的镜像View显示在屏幕上。

3、然后对touch事件进行处理,在手势移动的时候我们更新WindowManager中View的位置(也就是移动),并且根据移动的位置来处理gridview其它item位置的变化。

4、在手势移动结束后、抬起手势时,我们清理镜像,gridview交互数据,刷新视图。


实现代码

说了这么多,来看看怎么实现,先看下我们的自定义gridView:

package com.test.drag.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.PixelFormat;import android.os.Vibrator;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.WindowManager;import android.view.animation.Animation;import android.view.animation.TranslateAnimation;import android.widget.AdapterView;import android.widget.GridView;import android.widget.ImageView;import android.widget.TextView;import com.test.drag.DragAdapter;import com.test.drag.R;/** * Created by fuweiwei on 2016/1/9. */public class DragGridView extends GridView{    /** 点击时候的X位置 */    public int downX;    /** 点击时候的Y位置 */    public int downY;    /** 点击时候对应整个界面的X位置 */    public int windowX;    /** 点击时候对应整个界面的Y位置 */    public int windowY;    /** VIEW相对自己的X */    private int win_view_x;    /**VIEW相对自己的Y*/    private int win_view_y;    /** 长按时候对应postion */    public int dragPosition;    /** Up后对应的ITEM的Position */    private int dropPosition;    /** 开始拖动的ITEM的Position*/    private int startPosition;    /** item高 */    private int itemHeight;    /** item宽 */    private int itemWidth;    /** 拖动的时候对应ITEM的VIEW */    private View dragImageView = null;    /** 长按的时候ITEM的VIEW*/    private ViewGroup dragItemView = null;    /** WindowManager管理器 */    private WindowManager windowManager = null;    /** */    private WindowManager.LayoutParams windowParams = null;    /** item总量*/    private int itemTotalCount;    /** 一行的ITEM数量*/    private int nColumns = 4;    /** 行数 */    private int nRows;    /** 剩余部分 */    private int Remainder;    /** 是否在移动 */    private boolean isMoving = false;    /** */    private int holdPosition;    /** 拖动的时候放大的倍数 */    private double dragScale = 1.2D;    /** 震动器  */    private Vibrator mVibrator;    /** 每个ITEM之间的水平间距 */    private int mHorizontalSpacing = 15;    /** 每个ITEM之间的竖直间距 */    private int mVerticalSpacing = 15;    /** 移动时候最后个动画的ID */    private String LastAnimationID;    public DragGridView(Context context) {        super(context);        init(context);    }    public DragGridView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    public DragGridView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    /**     * dip转为 px     */    public  int dip2px(Context context, float dipValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dipValue * scale + 0.5f);    }    public void init(Context context){        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);        //将布局文件中设置的间距dip转为px        mHorizontalSpacing = dip2px(context, mHorizontalSpacing);    }    /** 在ScrollView内,所以要进行计算高度 */    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            downX = (int) ev.getX();            downY = (int) ev.getY();            windowX = (int) ev.getX();            windowY = (int) ev.getY();            setOnItemClickListener(ev);        }        return super.dispatchTouchEvent(ev);    }    /** 停止拖动 ,释放并初始化 */    private void stopDrag() {        if (dragImageView != null) {            windowManager.removeView(dragImageView);            dragImageView = null;        }    }    /** 在拖动的情况更新View的位置 */    private void onDrag(int x, int y , int rawx , int rawy) {        if (dragImageView != null) {            windowParams.alpha = 0.6f;            windowParams.x = rawx - win_view_x;            windowParams.y = rawy - win_view_y;            windowManager.updateViewLayout(dragImageView, windowParams);        }    }    /**     *创建窗口对象、添加我们要移动的View     * @param dragBitmap     * @param x     * @param y     */    public void startDrag(Bitmap dragBitmap, int x, int y) {        stopDrag();        windowParams = new WindowManager.LayoutParams();// 获取WINDOW界面的        //Gravity.TOP|Gravity.LEFT;这个必须加        windowParams.gravity = Gravity.TOP | Gravity.LEFT;        //得到要移动的View左上角相对于屏幕的坐标        windowParams.x = x - win_view_x;        windowParams.y = y  - win_view_y;        //设置拖拽item的宽和高        windowParams.width = (int) (dragScale * dragBitmap.getWidth());// 放大dragScale倍,可以设置拖动后的倍数        windowParams.height = (int) (dragScale * dragBitmap.getHeight());// 放大dragScale倍,可以设置拖动后的倍数        this.windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;        this.windowParams.format = PixelFormat.TRANSLUCENT;        this.windowParams.windowAnimations = 0;        ImageView iv = new ImageView(getContext());        iv.setImageBitmap(dragBitmap);        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// "window"        windowManager.addView(iv, windowParams);        dragImageView = iv;    }    /** 隐藏 放下 的ITEM*/    private void hideDropItem() {        ((DragAdapter) getAdapter()).setShowDropItem(false);    }    /** 在松手下放的情况,更新界面 */    private void onDrop(int x, int y) {        // 根据拖动到的x,y坐标获取拖动位置下方的ITEM对应的POSTION        int tempPostion = pointToPosition(x, y);        dropPosition = tempPostion;        DragAdapter mDragAdapter = (DragAdapter) getAdapter();        //显示刚拖动的ITEM        mDragAdapter.setShowDropItem(true);        //刷新适配器,让对应的ITEM显示        mDragAdapter.notifyDataSetChanged();    }    /**     * 长按点击监听     * @param ev     */    public void setOnItemClickListener(final MotionEvent ev) {        setOnItemLongClickListener(new OnItemLongClickListener() {            @Override            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {                int x = (int) ev.getX();// 长按事件的X位置                int y = (int) ev.getY();// 长按事件的y位置                startPosition = position;// 第一次点击的postion                dragPosition = position;                if (startPosition <= 1) {//前2个默认不点击、可以设置                    return false;                }                ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition - getFirstVisiblePosition());                TextView dragTextView = (TextView)dragViewGroup.findViewById(R.id.text_item);                dragTextView.setSelected(true);                dragTextView.setEnabled(false);                itemHeight = dragViewGroup.getHeight();                itemWidth = dragViewGroup.getWidth();                itemTotalCount = DragGridView.this.getCount();                // 如果特殊的这个不等于拖动的那个,并且不等于-1                if (dragPosition != AdapterView.INVALID_POSITION) {                    // 释放的资源使用的绘图缓存。如果你调用buildDrawingCache()手动没有调用setDrawingCacheEnabled(真正的),你应该清理缓存使用这种方法。                    win_view_x = windowX - dragViewGroup.getLeft();//VIEW相对自己的X,半斤                    win_view_y = windowY - dragViewGroup.getTop();//VIEW相对自己的y,半斤                    dragItemView = dragViewGroup;                    dragViewGroup.destroyDrawingCache();                    dragViewGroup.setDrawingCacheEnabled(true);                    Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup.getDrawingCache());                    mVibrator.vibrate(50);//设置震动时间                    startDrag(dragBitmap, (int)ev.getRawX(),  (int)ev.getRawY());                    hideDropItem();                    dragViewGroup.setVisibility(View.INVISIBLE);                    isMoving = false;                    return true;                }                return false;            }        });    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (dragImageView != null && dragPosition != AdapterView.INVALID_POSITION) {            // 移动时候的对应x,y位置            int x = (int) ev.getX();            int y = (int) ev.getY();            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    downX = (int) ev.getX();                    windowX = (int) ev.getX();                    downY = (int) ev.getY();                    windowY = (int) ev.getY();                    break;                case MotionEvent.ACTION_MOVE:                    onDrag(x, y ,(int) ev.getRawX() , (int) ev.getRawY());                    if (!isMoving){                        OnMove(x, y);                    }                    if (pointToPosition(x, y) != AdapterView.INVALID_POSITION){                        break;                    }                    break;                case MotionEvent.ACTION_UP:                    stopDrag();                    onDrop(x, y);                    requestDisallowInterceptTouchEvent(false);                    break;                default:                    break;            }        }        return super.onTouchEvent(ev);    }    /** 移动的时候触发,移动所有改变的Item*/    public void OnMove(int x, int y) {        // 拖动的VIEW下方的POSTION        int dPosition = pointToPosition(x, y);        // 判断下方的POSTION是否是最开始2个不能拖动的        if (dPosition > 1) {            if ((dPosition == -1) || (dPosition == dragPosition)){                return;            }            dropPosition = dPosition;            if (dragPosition != startPosition){                dragPosition = startPosition;            }            int movecount;            //拖动的=开始拖的,并且 拖动的 不等于放下的            if ((dragPosition == startPosition) || (dragPosition != dropPosition)){                //移需要移动的动ITEM数量                movecount = dropPosition - dragPosition;            }else{                //移需要移动的动ITEM数量为0                movecount = 0;            }            if(movecount == 0){                return;            }            int movecount_abs = Math.abs(movecount);            if (dPosition != dragPosition) {                //dragGroup设置为不可见                ViewGroup dragGroup = (ViewGroup) getChildAt(dragPosition);                dragGroup.setVisibility(View.INVISIBLE);                float to_x = 1;//移动的X偏移量                float to_y;// 移动的Y偏移量                //x_vlaue移动的距离百分比(相对于自己长度的百分比)                float x_vlaue = ((float) mHorizontalSpacing / (float) itemWidth) + 1.0f;                //y_vlaue移动的距离百分比(相对于自己宽度的百分比)                float y_vlaue = ((float) mVerticalSpacing / (float) itemHeight) + 1.0f;                Log.d("x_vlaue", "x_vlaue = " + x_vlaue);                for (int i = 0; i < movecount_abs; i++) {                    to_x = x_vlaue;                    to_y = y_vlaue;                    //向右                    if (movecount > 0) {                        // 判断是不是同一行的                        holdPosition = dragPosition + i + 1;                        if (dragPosition / nColumns == holdPosition / nColumns) {                            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{                        //向左,下移到上,右移到左                        holdPosition = dragPosition - i - 1;                        if (dragPosition / nColumns == holdPosition / nColumns) {                            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);                    Animation moveAnimation = getMoveAnimation(to_x, to_y);                    moveViewGroup.startAnimation(moveAnimation);                    //如果是最后一个移动的,那么设置他的最后个动画ID为LastAnimationID                    if (holdPosition == dropPosition) {                        LastAnimationID = moveAnimation.toString();                    }                    moveAnimation.setAnimationListener(new Animation.AnimationListener() {                        @Override                        public void onAnimationStart(Animation animation) {                            isMoving = true;                        }                        @Override                        public void onAnimationRepeat(Animation animation) {                        }                        @Override                        public void onAnimationEnd(Animation animation) {                            // 如果为最后个动画结束,那执行下面的方法                            if (animation.toString().equalsIgnoreCase(LastAnimationID)) {                                DragAdapter mDragAdapter = (DragAdapter) getAdapter();                                mDragAdapter.exchange(startPosition,dropPosition);                                startPosition = dropPosition;                                dragPosition = dropPosition;                                isMoving = false;                            }                        }                    });                }            }        }    }    /** 获取移动的动画 */    public Animation getMoveAnimation(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(true);// 设置一个动画效果执行完毕后,View对象保留在终止的位置。        mTranslateAnimation.setDuration(300L);        return mTranslateAnimation;    }}

适配器代码:

package com.test.drag;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.List;public class DragAdapter extends BaseAdapter {        /** TAG*/        private final static String TAG = "DragAdapter";        /** 是否显示底部的ITEM */        private boolean isItemShow = false;        private Context context;        /** 控制的postion */        private int holdPosition;        /** 是否改变 */        private boolean isChanged = false;        /** 列表数据是否改变 */        private boolean isListChanged = false;        /** 是否可见 */        boolean isVisible = true;        /** 可以拖动的列表(即用户选择的频道列表) */        public List<String> channelList;        /** TextView 频道内容 */        private TextView item_text;        /** 要删除的position */        public int remove_position = -1;        /** 是否是用户频道 */        private boolean isUser = false;        public DragAdapter(Context context, List<String> channelList,boolean isUser) {                this.context = context;                this.channelList = channelList;                this.isUser = isUser;        }                @Override        public int getCount() {                // TODO Auto-generated method stub                return channelList == null ? 0 : channelList.size();        }        @Override        public String getItem(int position) {                // TODO Auto-generated method stub                if (channelList != null && channelList.size() != 0) {                        return channelList.get(position);                }                return null;        }        @Override        public long getItemId(int position) {                // TODO Auto-generated method stub                return position;        }                @Override        public View getView(int position, View convertView, ViewGroup parent) {                View view = LayoutInflater.from(context).inflate(R.layout.adapter_mygridview_item, null);                item_text = (TextView) view.findViewById(R.id.text_item);                String channel = getItem(position);                item_text.setText(channel);                if(isUser){                        if ((position == 0) || (position == 1)){                                item_text.setEnabled(false);                        }                }                if (isChanged && (position == holdPosition) && !isItemShow) {                        item_text.setText("");                        item_text.setSelected(true);                        item_text.setEnabled(true);                        isChanged = false;                }                if (!isVisible && (position == -1 + channelList.size())) {                        item_text.setText("");                        item_text.setSelected(true);                        item_text.setEnabled(true);                }                if(remove_position == position){                        item_text.setText("");                }                return view;        }        /** 添加频道列表 */        public void addItem(String channel) {                channelList.add(channel);                isListChanged = true;                notifyDataSetChanged();        }        /** 拖动变更频道排序 */        public void exchange(int dragPostion, int dropPostion) {                holdPosition = dropPostion;                String dragItem = getItem(dragPostion);                Log.d(TAG, "startPostion=" + dragPostion + ";endPosition=" + dropPostion);                if (dragPostion < dropPostion) {                        channelList.add(dropPostion + 1, dragItem);                        channelList.remove(dragPostion);                } else {                        channelList.add(dropPostion, dragItem);                        channelList.remove(dragPostion + 1);                }                isChanged = true;                isListChanged = true;                notifyDataSetChanged();        }                /** 获取频道列表 */        public List<String> getChannnelLst() {                return channelList;        }        /** 设置删除的position */        public void setRemove(int position) {                remove_position = position;                notifyDataSetChanged();        }        /** 删除频道列表 */        public void remove() {                channelList.remove(remove_position);                remove_position = -1;                isListChanged = true;                notifyDataSetChanged();        }                /** 设置频道列表 */        public void setListDate(List<String> list) {                channelList = list;        }                /** 获取是否可见 */        public boolean isVisible() {                return isVisible;        }                /** 排序是否发生改变 */        public boolean isListChanged() {                return isListChanged;        }                /** 设置是否可见 */        public void setVisible(boolean visible) {                isVisible = visible;        }        /** 显示放下的ITEM */        public void setShowDropItem(boolean show) {                isItemShow = show;        }}


代码比较多,我一 一解释下:

dispatchTouchEvent():因为gridview也是继承ViewGroup的,覆写它的事件分发方法。在这里设置长按事件监听。

startDarg():根据长按获取的Item,获取缓存对象Bitmap,new一个Imageview代替Item,然后创建我们的窗口对象WindowManager ,把Imageview添加到我们的WindowManager中。

hideDropItem():隐藏我们长按的Item,因为我们已经用WindowManager来代替我们的Item。

onTouchEvent():对分发来的事件进行处理,手势移动的时候,进行相应的操作。

onDrag():移动的时候刷新WindowManager中View的位置。

onMove():根据手势移动的位置,获取X、Y坐标,判断移动到了gridview的哪个位置、是向左移动、还是向右移动、是否处于同一行,因为取代了其它Item的位置,会导致一些item的位置也会发生改变,所以我们要移动改变位置了的Item,里面的逻辑有点复杂,不过我都带代码里注释了,很容易看懂。

onDrop():当手势移动完毕后、抬起手势时、我们就显示移动时隐藏的Item。

stopDrop():移动完毕后、我们要清除WindowManager中的View,防止重复。


适配器中的代码跟上一篇博客中适配器基本没什么不同,就是加了一个交换数据的方法exchange(),认真看的也很好理解,就是当移动item的位置大于初始位置时,就是把移动的item放到后面一个位置,然后删除之前的item,这样位置就发生变化,反之也是一样的道理。


效果如下:




其实只要思路正确了,我们就有方法实现以比较难的功能了,网上也有很多其他的方法来实现这个功能,也可以多看看别人是怎么实现的,自己动手写,对自己帮助还是很大的,我这个也是参考网上一些资料改造的,有什么问题,欢迎大家指正,下面有项目源码,Android studio环境。

前言

上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动,详情请参考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和拖拽)

今天把相对比较复杂的gridView的拖拽也记录下,在开始之前我们事先要了解下Android的事件分发机制,网上这方面的资料也比较多。因为自定义控件大部分要用到事件分发机制的知识。

实现思路

要实现Item的拖拽,其实并不是真正要去拖拽GridView的Item,而是使用WindowManager添加一个Item的镜像来代替Item,进行移动拖拽的操作。

1、首先我们对touch事件进行分发,设置长按监听器。

2、获取我们长按的Item 隐藏它,然后用WindowManager添加一个Item的镜像View显示在屏幕上。

3、然后对touch事件进行处理,在手势移动的时候我们更新WindowManager中View的位置(也就是移动),并且根据移动的位置来处理gridview其它item位置的变化。

4、在手势移动结束后、抬起手势时,我们清理镜像,gridview交互数据,刷新视图。


实现代码

说了这么多,来看看怎么实现,先看下我们的自定义gridView:

package com.test.drag.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.PixelFormat;import android.os.Vibrator;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.WindowManager;import android.view.animation.Animation;import android.view.animation.TranslateAnimation;import android.widget.AdapterView;import android.widget.GridView;import android.widget.ImageView;import android.widget.TextView;import com.test.drag.DragAdapter;import com.test.drag.R;/** * Created by fuweiwei on 2016/1/9. */public class DragGridView extends GridView{    /** 点击时候的X位置 */    public int downX;    /** 点击时候的Y位置 */    public int downY;    /** 点击时候对应整个界面的X位置 */    public int windowX;    /** 点击时候对应整个界面的Y位置 */    public int windowY;    /** VIEW相对自己的X */    private int win_view_x;    /**VIEW相对自己的Y*/    private int win_view_y;    /** 长按时候对应postion */    public int dragPosition;    /** Up后对应的ITEM的Position */    private int dropPosition;    /** 开始拖动的ITEM的Position*/    private int startPosition;    /** item高 */    private int itemHeight;    /** item宽 */    private int itemWidth;    /** 拖动的时候对应ITEM的VIEW */    private View dragImageView = null;    /** 长按的时候ITEM的VIEW*/    private ViewGroup dragItemView = null;    /** WindowManager管理器 */    private WindowManager windowManager = null;    /** */    private WindowManager.LayoutParams windowParams = null;    /** item总量*/    private int itemTotalCount;    /** 一行的ITEM数量*/    private int nColumns = 4;    /** 行数 */    private int nRows;    /** 剩余部分 */    private int Remainder;    /** 是否在移动 */    private boolean isMoving = false;    /** */    private int holdPosition;    /** 拖动的时候放大的倍数 */    private double dragScale = 1.2D;    /** 震动器  */    private Vibrator mVibrator;    /** 每个ITEM之间的水平间距 */    private int mHorizontalSpacing = 15;    /** 每个ITEM之间的竖直间距 */    private int mVerticalSpacing = 15;    /** 移动时候最后个动画的ID */    private String LastAnimationID;    public DragGridView(Context context) {        super(context);        init(context);    }    public DragGridView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    public DragGridView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    /**     * dip转为 px     */    public  int dip2px(Context context, float dipValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dipValue * scale + 0.5f);    }    public void init(Context context){        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);        //将布局文件中设置的间距dip转为px        mHorizontalSpacing = dip2px(context, mHorizontalSpacing);    }    /** 在ScrollView内,所以要进行计算高度 */    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            downX = (int) ev.getX();            downY = (int) ev.getY();            windowX = (int) ev.getX();            windowY = (int) ev.getY();            setOnItemClickListener(ev);        }        return super.dispatchTouchEvent(ev);    }    /** 停止拖动 ,释放并初始化 */    private void stopDrag() {        if (dragImageView != null) {            windowManager.removeView(dragImageView);            dragImageView = null;        }    }    /** 在拖动的情况更新View的位置 */    private void onDrag(int x, int y , int rawx , int rawy) {        if (dragImageView != null) {            windowParams.alpha = 0.6f;            windowParams.x = rawx - win_view_x;            windowParams.y = rawy - win_view_y;            windowManager.updateViewLayout(dragImageView, windowParams);        }    }    /**     *创建窗口对象、添加我们要移动的View     * @param dragBitmap     * @param x     * @param y     */    public void startDrag(Bitmap dragBitmap, int x, int y) {        stopDrag();        windowParams = new WindowManager.LayoutParams();// 获取WINDOW界面的        //Gravity.TOP|Gravity.LEFT;这个必须加        windowParams.gravity = Gravity.TOP | Gravity.LEFT;        //得到要移动的View左上角相对于屏幕的坐标        windowParams.x = x - win_view_x;        windowParams.y = y  - win_view_y;        //设置拖拽item的宽和高        windowParams.width = (int) (dragScale * dragBitmap.getWidth());// 放大dragScale倍,可以设置拖动后的倍数        windowParams.height = (int) (dragScale * dragBitmap.getHeight());// 放大dragScale倍,可以设置拖动后的倍数        this.windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;        this.windowParams.format = PixelFormat.TRANSLUCENT;        this.windowParams.windowAnimations = 0;        ImageView iv = new ImageView(getContext());        iv.setImageBitmap(dragBitmap);        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// "window"        windowManager.addView(iv, windowParams);        dragImageView = iv;    }    /** 隐藏 放下 的ITEM*/    private void hideDropItem() {        ((DragAdapter) getAdapter()).setShowDropItem(false);    }    /** 在松手下放的情况,更新界面 */    private void onDrop(int x, int y) {        // 根据拖动到的x,y坐标获取拖动位置下方的ITEM对应的POSTION        int tempPostion = pointToPosition(x, y);        dropPosition = tempPostion;        DragAdapter mDragAdapter = (DragAdapter) getAdapter();        //显示刚拖动的ITEM        mDragAdapter.setShowDropItem(true);        //刷新适配器,让对应的ITEM显示        mDragAdapter.notifyDataSetChanged();    }    /**     * 长按点击监听     * @param ev     */    public void setOnItemClickListener(final MotionEvent ev) {        setOnItemLongClickListener(new OnItemLongClickListener() {            @Override            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {                int x = (int) ev.getX();// 长按事件的X位置                int y = (int) ev.getY();// 长按事件的y位置                startPosition = position;// 第一次点击的postion                dragPosition = position;                if (startPosition <= 1) {//前2个默认不点击、可以设置                    return false;                }                ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition - getFirstVisiblePosition());                TextView dragTextView = (TextView)dragViewGroup.findViewById(R.id.text_item);                dragTextView.setSelected(true);                dragTextView.setEnabled(false);                itemHeight = dragViewGroup.getHeight();                itemWidth = dragViewGroup.getWidth();                itemTotalCount = DragGridView.this.getCount();                // 如果特殊的这个不等于拖动的那个,并且不等于-1                if (dragPosition != AdapterView.INVALID_POSITION) {                    // 释放的资源使用的绘图缓存。如果你调用buildDrawingCache()手动没有调用setDrawingCacheEnabled(真正的),你应该清理缓存使用这种方法。                    win_view_x = windowX - dragViewGroup.getLeft();//VIEW相对自己的X,半斤                    win_view_y = windowY - dragViewGroup.getTop();//VIEW相对自己的y,半斤                    dragItemView = dragViewGroup;                    dragViewGroup.destroyDrawingCache();                    dragViewGroup.setDrawingCacheEnabled(true);                    Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup.getDrawingCache());                    mVibrator.vibrate(50);//设置震动时间                    startDrag(dragBitmap, (int)ev.getRawX(),  (int)ev.getRawY());                    hideDropItem();                    dragViewGroup.setVisibility(View.INVISIBLE);                    isMoving = false;                    return true;                }                return false;            }        });    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (dragImageView != null && dragPosition != AdapterView.INVALID_POSITION) {            // 移动时候的对应x,y位置            int x = (int) ev.getX();            int y = (int) ev.getY();            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    downX = (int) ev.getX();                    windowX = (int) ev.getX();                    downY = (int) ev.getY();                    windowY = (int) ev.getY();                    break;                case MotionEvent.ACTION_MOVE:                    onDrag(x, y ,(int) ev.getRawX() , (int) ev.getRawY());                    if (!isMoving){                        OnMove(x, y);                    }                    if (pointToPosition(x, y) != AdapterView.INVALID_POSITION){                        break;                    }                    break;                case MotionEvent.ACTION_UP:                    stopDrag();                    onDrop(x, y);                    requestDisallowInterceptTouchEvent(false);                    break;                default:                    break;            }        }        return super.onTouchEvent(ev);    }    /** 移动的时候触发,移动所有改变的Item*/    public void OnMove(int x, int y) {        // 拖动的VIEW下方的POSTION        int dPosition = pointToPosition(x, y);        // 判断下方的POSTION是否是最开始2个不能拖动的        if (dPosition > 1) {            if ((dPosition == -1) || (dPosition == dragPosition)){                return;            }            dropPosition = dPosition;            if (dragPosition != startPosition){                dragPosition = startPosition;            }            int movecount;            //拖动的=开始拖的,并且 拖动的 不等于放下的            if ((dragPosition == startPosition) || (dragPosition != dropPosition)){                //移需要移动的动ITEM数量                movecount = dropPosition - dragPosition;            }else{                //移需要移动的动ITEM数量为0                movecount = 0;            }            if(movecount == 0){                return;            }            int movecount_abs = Math.abs(movecount);            if (dPosition != dragPosition) {                //dragGroup设置为不可见                ViewGroup dragGroup = (ViewGroup) getChildAt(dragPosition);                dragGroup.setVisibility(View.INVISIBLE);                float to_x = 1;//移动的X偏移量                float to_y;// 移动的Y偏移量                //x_vlaue移动的距离百分比(相对于自己长度的百分比)                float x_vlaue = ((float) mHorizontalSpacing / (float) itemWidth) + 1.0f;                //y_vlaue移动的距离百分比(相对于自己宽度的百分比)                float y_vlaue = ((float) mVerticalSpacing / (float) itemHeight) + 1.0f;                Log.d("x_vlaue", "x_vlaue = " + x_vlaue);                for (int i = 0; i < movecount_abs; i++) {                    to_x = x_vlaue;                    to_y = y_vlaue;                    //向右                    if (movecount > 0) {                        // 判断是不是同一行的                        holdPosition = dragPosition + i + 1;                        if (dragPosition / nColumns == holdPosition / nColumns) {                            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{                        //向左,下移到上,右移到左                        holdPosition = dragPosition - i - 1;                        if (dragPosition / nColumns == holdPosition / nColumns) {                            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);                    Animation moveAnimation = getMoveAnimation(to_x, to_y);                    moveViewGroup.startAnimation(moveAnimation);                    //如果是最后一个移动的,那么设置他的最后个动画ID为LastAnimationID                    if (holdPosition == dropPosition) {                        LastAnimationID = moveAnimation.toString();                    }                    moveAnimation.setAnimationListener(new Animation.AnimationListener() {                        @Override                        public void onAnimationStart(Animation animation) {                            isMoving = true;                        }                        @Override                        public void onAnimationRepeat(Animation animation) {                        }                        @Override                        public void onAnimationEnd(Animation animation) {                            // 如果为最后个动画结束,那执行下面的方法                            if (animation.toString().equalsIgnoreCase(LastAnimationID)) {                                DragAdapter mDragAdapter = (DragAdapter) getAdapter();                                mDragAdapter.exchange(startPosition,dropPosition);                                startPosition = dropPosition;                                dragPosition = dropPosition;                                isMoving = false;                            }                        }                    });                }            }        }    }    /** 获取移动的动画 */    public Animation getMoveAnimation(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(true);// 设置一个动画效果执行完毕后,View对象保留在终止的位置。        mTranslateAnimation.setDuration(300L);        return mTranslateAnimation;    }}

适配器代码:

package com.test.drag;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.List;public class DragAdapter extends BaseAdapter {        /** TAG*/        private final static String TAG = "DragAdapter";        /** 是否显示底部的ITEM */        private boolean isItemShow = false;        private Context context;        /** 控制的postion */        private int holdPosition;        /** 是否改变 */        private boolean isChanged = false;        /** 列表数据是否改变 */        private boolean isListChanged = false;        /** 是否可见 */        boolean isVisible = true;        /** 可以拖动的列表(即用户选择的频道列表) */        public List<String> channelList;        /** TextView 频道内容 */        private TextView item_text;        /** 要删除的position */        public int remove_position = -1;        /** 是否是用户频道 */        private boolean isUser = false;        public DragAdapter(Context context, List<String> channelList,boolean isUser) {                this.context = context;                this.channelList = channelList;                this.isUser = isUser;        }                @Override        public int getCount() {                // TODO Auto-generated method stub                return channelList == null ? 0 : channelList.size();        }        @Override        public String getItem(int position) {                // TODO Auto-generated method stub                if (channelList != null && channelList.size() != 0) {                        return channelList.get(position);                }                return null;        }        @Override        public long getItemId(int position) {                // TODO Auto-generated method stub                return position;        }                @Override        public View getView(int position, View convertView, ViewGroup parent) {                View view = LayoutInflater.from(context).inflate(R.layout.adapter_mygridview_item, null);                item_text = (TextView) view.findViewById(R.id.text_item);                String channel = getItem(position);                item_text.setText(channel);                if(isUser){                        if ((position == 0) || (position == 1)){                                item_text.setEnabled(false);                        }                }                if (isChanged && (position == holdPosition) && !isItemShow) {                        item_text.setText("");                        item_text.setSelected(true);                        item_text.setEnabled(true);                        isChanged = false;                }                if (!isVisible && (position == -1 + channelList.size())) {                        item_text.setText("");                        item_text.setSelected(true);                        item_text.setEnabled(true);                }                if(remove_position == position){                        item_text.setText("");                }                return view;        }        /** 添加频道列表 */        public void addItem(String channel) {                channelList.add(channel);                isListChanged = true;                notifyDataSetChanged();        }        /** 拖动变更频道排序 */        public void exchange(int dragPostion, int dropPostion) {                holdPosition = dropPostion;                String dragItem = getItem(dragPostion);                Log.d(TAG, "startPostion=" + dragPostion + ";endPosition=" + dropPostion);                if (dragPostion < dropPostion) {                        channelList.add(dropPostion + 1, dragItem);                        channelList.remove(dragPostion);                } else {                        channelList.add(dropPostion, dragItem);                        channelList.remove(dragPostion + 1);                }                isChanged = true;                isListChanged = true;                notifyDataSetChanged();        }                /** 获取频道列表 */        public List<String> getChannnelLst() {                return channelList;        }        /** 设置删除的position */        public void setRemove(int position) {                remove_position = position;                notifyDataSetChanged();        }        /** 删除频道列表 */        public void remove() {                channelList.remove(remove_position);                remove_position = -1;                isListChanged = true;                notifyDataSetChanged();        }                /** 设置频道列表 */        public void setListDate(List<String> list) {                channelList = list;        }                /** 获取是否可见 */        public boolean isVisible() {                return isVisible;        }                /** 排序是否发生改变 */        public boolean isListChanged() {                return isListChanged;        }                /** 设置是否可见 */        public void setVisible(boolean visible) {                isVisible = visible;        }        /** 显示放下的ITEM */        public void setShowDropItem(boolean show) {                isItemShow = show;        }}


代码比较多,我一 一解释下:

dispatchTouchEvent():因为gridview也是继承ViewGroup的,覆写它的事件分发方法。在这里设置长按事件监听。

startDarg():根据长按获取的Item,获取缓存对象Bitmap,new一个Imageview代替Item,然后创建我们的窗口对象WindowManager ,把Imageview添加到我们的WindowManager中。

hideDropItem():隐藏我们长按的Item,因为我们已经用WindowManager来代替我们的Item。

onTouchEvent():对分发来的事件进行处理,手势移动的时候,进行相应的操作。

onDrag():移动的时候刷新WindowManager中View的位置。

onMove():根据手势移动的位置,获取X、Y坐标,判断移动到了gridview的哪个位置、是向左移动、还是向右移动、是否处于同一行,因为取代了其它Item的位置,会导致一些item的位置也会发生改变,所以我们要移动改变位置了的Item,里面的逻辑有点复杂,不过我都带代码里注释了,很容易看懂。

onDrop():当手势移动完毕后、抬起手势时、我们就显示移动时隐藏的Item。

stopDrop():移动完毕后、我们要清除WindowManager中的View,防止重复。


适配器中的代码跟上一篇博客中适配器基本没什么不同,就是加了一个交换数据的方法exchange(),认真看的也很好理解,就是当移动item的位置大于初始位置时,就是把移动的item放到后面一个位置,然后删除之前的item,这样位置就发生变化,反之也是一样的道理。


效果如下:




其实只要思路正确了,我们就有方法实现以比较难的功能了,网上也有很多其他的方法来实现这个功能,也可以多看看别人是怎么实现的,自己动手写,对自己帮助还是很大的,我这个也是参考网上一些资料改造的,有什么问题,欢迎大家指正,下面有项目源码,Android studio环境。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 引起嫂嫂裙子顶上去 婶子的哪里又紧水又多 哥哥好凶猛太大要撑坏 老师让学生趴小内内 两个阿姨一起陪我睡觉 朋友妻子来我家洗澡你家茜 老师 主动脱下内内 浪妇杨雪小说全集 同学的妻子在线播放 韩国朋友妻子在线播放 朋友坐牢替他照顾妻子在线播放 比我老婆还要正在线播放 朋友的妻子20在线播放 寂寞的妻子手机在线 朋友出差替他照顾妻子在线播放 偷朋友妻子在线播放 搞兄弟母亲中文字在线播放 隔壁的日本妻子1中文字 年轻的母亲5d整部中文字 老师强入我体内 嫂嫂 的房门没关紧 共享妻子第3季 小石玩妻子第三部 朋友母亲的味道中文版 互换朋友妻子3换 母亲的朋友2集中文免费观看 领导出差他老婆留我过夜 厨房偷上朋友妻全文阅读答案 被老公朋友强入bd 和领导回家睡了他老婆 老公在偷吃别人我在家吃他老爹 送领导回家强睡他老婆 我在老公的眼皮底下偷吃 快递员在我家要了我 30岁军嫂身体好软 当着女朋友的面睡他朋友视频 偷朋友的妻子电影中国字 老公朋友下厨房要了我 朋友打麻将我在楼上 下厨房里边做菜边啪 相隔不到5公尺偷捅妻子朋友