android ListView和GridView拖拽移位具体实现及拓展

来源:互联网 发布:优易数据交易平台 编辑:程序博客网 时间:2024/04/29 18:55



关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:

               

首先说一下:拖拽ListView的item就不应该可以任意移动,只应该在ListView所在的范围内,而网易的你看看我都可以移动到状态栏了,虽然你做了处理,但是用户体验我个人感觉不好,在看看百度的,不仅控制了移动范围,更不错的百度的移动起来会时时的换位,看起来相当的形象,所以我认为这样相当的棒.

说明一点,我没有那么有才,我也是看别人代码,然后自己整理下.在这里就简单记载一下.

首先对touch事件的处理,从应用中,我们可以得出,在我们点击后面拖拉图标后,就会创建一个item的影像视图.并且可以移动该影像,而此时的ListView不应该有touch事件.

onInterceptTouchEvent方法.

[java] view plaincopy

    /***
         * touch事件拦截  
         */  
        @Override  
        public boolean onInterceptTouchEvent(MotionEvent ev) {  
            // 按下  
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
                int x = (int) ev.getX();// 获取相对与ListView的x坐标  
                int y = (int) ev.getY();// 获取相应与ListView的y坐标  
                dragSrcPosition = dragPosition = pointToPosition(x, y);  
                // 无效不进行处理  
                if (dragPosition == AdapterView.INVALID_POSITION) {  
                    return super.onInterceptTouchEvent(ev);  
                }  
      
                // 获取当前位置的视图(可见状态)  
                ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
                        - getFirstVisiblePosition());  
      
                // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
                dragPoint = y - itemView.getTop();  
                // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
                dragOffset = (int) (ev.getRawY() - y);  
      
                // 获取可拖拽的图标  
                View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
      
                // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
                if (dragger != null && x > dragger.getLeft() - 20) {  
      
                    upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
                    downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
      
                    itemView.setDrawingCacheEnabled(true);// 开启cache.  
                    Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
                    startDrag(bm, y);// 初始化影像  
                }  
                // return false;  
            }  
      
            return super.onInterceptTouchEvent(ev);  
        }  

这个方法的作用很简单:当我们摁下的如果是可拖拽的图标,那么进行初始化该Item的映像试图.

而在这里如果大家对WindowManager和WindowManager.LayoutParams不熟悉的朋友先去参考下这篇文章,要对WindowManager有一定的了解,简单的会应用.

接下来我们看onTouchEvent事件:

[java] view plaincopy

    /**
         * 触摸事件处理
         */  
        @Override  
        public boolean onTouchEvent(MotionEvent ev) {  
            // item的view不为空,且获取的dragPosition有效  
            if (dragImageView != null && dragPosition != INVALID_POSITION) {  
                int action = ev.getAction();  
                switch (action) {  
                case MotionEvent.ACTION_UP:  
                    int upY = (int) ev.getY();  
                    stopDrag();  
                    onDrop(upY);  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    int moveY = (int) ev.getY();  
                    onDrag(moveY);  
      
                    break;  
                case MotionEvent.ACTION_DOWN:  
                    break;  
                default:  
                    break;  
                }  
                return true;// 取消ListView滑动.  
            }  
      
            return super.onTouchEvent(ev);  
        }  

简单说明:首先在Touch中,我们要进行判断,是否点击的是拖动图标,如果是的话,那么对ACTION_MOVE and ACTION_UP相应事件进行处理,并且返回true or false.作用:取消ListView自身的Touch事件.如果不是的话,执行ListView 本身的Touch事件.

大致就介绍这么多,具体的实现,还是大家看源码吧,我注释的还算清晰,只要大家仔细看的话,一定可以掌握的,为什么这么说呢,技术只有在掌握了情况下才可以进行拓展.

对了,提醒大家要理解这三句话:
getRawX()和getRawY():获得的是相对屏幕的位置.
getX()和getY():获得的永远是相对view的触摸位置 坐标(这两个值不会超过view的长度和宽度)。
getLeft , getTop, getBottom,getRight, 这个指的是该控件相对于父控件的距离.
源码:

[java] view plaincopy

    package com.jj.drag;  
      
    import android.content.Context;  
    import android.graphics.Bitmap;  
    import android.os.AsyncTask;  
    import android.util.AttributeSet;  
    import android.util.Log;  
    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.AbsListView;  
    import android.widget.AbsListView.OnScrollListener;  
    import android.widget.AdapterView;  
    import android.widget.ImageView;  
    import android.widget.ListView;  
      
    import com.jj.drag.MainActivity.DragListAdapter;  
      
    /***
     * 自定义拖拽ListView
     *  
     * @author zhangjia
     *  
     */  
    public class DragListView extends ListView {  
      
        private WindowManager windowManager;// windows窗口控制类  
        private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
      
        private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
      
        private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
        private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
        private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
      
        private int dragPoint;// 在当前数据项中的位置  
        private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上)  
      
        private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
        private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
      
        private final static int step = 1;// ListView 滑动步伐.  
      
        private int current_Step;// 当前步伐.  
      
        /***
         * 构造方法
         *  
         * @param context
         * @param attrs
         */  
        public DragListView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
        }  
      
        /***
         * touch事件拦截
         */  
        @Override  
        public boolean onInterceptTouchEvent(MotionEvent ev) {  
            // 按下  
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
                int x = (int) ev.getX();// 获取相对与ListView的x坐标  
                int y = (int) ev.getY();// 获取相应与ListView的y坐标  
                dragSrcPosition = dragPosition = pointToPosition(x, y);  
                // 无效不进行处理  
                if (dragPosition == AdapterView.INVALID_POSITION) {  
                    return super.onInterceptTouchEvent(ev);  
                }  
      
                // 获取当前位置的视图(可见状态)  
                ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
                        - getFirstVisiblePosition());  
      
                // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
                dragPoint = y - itemView.getTop();  
                // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
                dragOffset = (int) (ev.getRawY() - y);  
      
                // 获取可拖拽的图标  
                View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
      
                // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
                if (dragger != null && x > dragger.getLeft() - 20) {  
      
                    upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
                    downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
      
                    itemView.setDrawingCacheEnabled(true);// 开启cache.  
                    Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
                    startDrag(bm, y);// 初始化影像  
                }  
            }  
      
            return super.onInterceptTouchEvent(ev);  
        }  
      
        /**
         * 触摸事件处理
         */  
        @Override  
        public boolean onTouchEvent(MotionEvent ev) {  
            // item的view不为空,且获取的dragPosition有效  
            if (dragImageView != null && dragPosition != INVALID_POSITION) {  
                int action = ev.getAction();  
                switch (action) {  
                case MotionEvent.ACTION_UP:  
                    int upY = (int) ev.getY();  
                    stopDrag();  
                    onDrop(upY);  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    int moveY = (int) ev.getY();  
                    onDrag(moveY);  
                    break;  
                case MotionEvent.ACTION_DOWN:  
                    break;  
                default:  
                    break;  
                }  
                return true;// 取消ListView滑动.  
            }  
      
            return super.onTouchEvent(ev);  
        }  
      
        /**
         * 准备拖动,初始化拖动项的图像
         *  
         * @param bm
         * @param y
         */  
        private void startDrag(Bitmap bm, int y) {  
            // stopDrag();  
            /***
             * 初始化window.
             */  
            windowParams = new WindowManager.LayoutParams();  
            windowParams.gravity = Gravity.TOP;  
            windowParams.x = 0;  
            windowParams.y = y - dragPoint + dragOffset;  
            windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
            windowParams.height = 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 imageView = new ImageView(getContext());  
            imageView.setImageBitmap(bm);  
            windowManager = (WindowManager) getContext().getSystemService("window");  
            windowManager.addView(imageView, windowParams);  
            dragImageView = imageView;  
      
        }  
      
        /**
         * 拖动执行,在Move方法中执行
         *  
         * @param y
         */  
        public void onDrag(int y) {  
            int drag_top = y - dragPoint;// 拖拽view的top值不能<0,否则则出界.  
            if (dragImageView != null && drag_top >= 0) {  
                windowParams.alpha = 0.5f;// 透明度  
                windowParams.y = y - dragPoint + dragOffset;// 移动y值.//记得要加上dragOffset,windowManager计算的是整个屏幕.(标题栏和状态栏都要算上)  
                windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动.  
            }  
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(0, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
      
            }  
            doScroller(y);  
        }  
      
        /***
         * ListView的移动.
         * 要明白移动原理:当映像移动到下端的时候,ListView向上滑动,当映像移动到上端的时候,ListView要向下滑动。正好和实际的相反.
         *  
         */  
      
        public void doScroller(int y) {  
            Log.e("jj", "y=" + y);  
            Log.e("jj", "upScrollBounce=" + upScrollBounce);  
            // ListView需要下滑  
            if (y < upScrollBounce) {  
                current_Step = step + (upScrollBounce - y) / 10;// 时时步伐  
            }// ListView需要上滑  
            else if (y > downScrollBounce) {  
                current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐  
            } else {  
                current_Step = 0;  
            }  
      
            // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)  
            View view = getChildAt(dragPosition - getFirstVisiblePosition());  
            // 真正滚动的方法setSelectionFromTop()  
            setSelectionFromTop(dragPosition, view.getTop() + current_Step);  
      
        }  
      
        /**
         * 停止拖动,删除影像
         */  
        public void stopDrag() {  
            if (dragImageView != null) {  
                windowManager.removeView(dragImageView);  
                dragImageView = null;  
            }  
        }  
      
        /**
         * 拖动放下的时候
         *  
         * @param y
         */  
        public void onDrop(int y) {  
      
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(0, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
            }  
      
            // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)  
            if (y < getChildAt(0).getTop()) {  
                // 超出上边界  
                dragPosition = 0;  
                // 如果拖动超过最后一项的最下边那么就防止在最下边  
            } else if (y > getChildAt(getChildCount() - 1).getBottom()) {  
                // 超出下边界  
                dragPosition = getAdapter().getCount() - 1;  
            }  
      
            // 数据交换  
            if (dragPosition < getAdapter().getCount()) {  
                DragListAdapter adapter = (DragListAdapter) getAdapter();  
                adapter.update(dragSrcPosition, dragPosition);  
            }  
      
        }  
      
    }  

下面我说下适配器:

[java] view plaincopy

    /***
         * 自定义适配器
         *  
         * @author zhangjia
         *  
         */  
        class DragListAdapter extends BaseAdapter {  
            private ArrayList<String> arrayTitles;  
            private ArrayList<Integer> arrayDrawables;  
            private Context context;  
      
            public DragListAdapter(Context context, ArrayList<String> arrayTitles,  
                    ArrayList<Integer> arrayDrawables) {  
                this.context = context;  
                this.arrayTitles = arrayTitles;  
                this.arrayDrawables = arrayDrawables;  
            }  
      
            @Override  
            public View getView(int position, View convertView, ViewGroup parent) {  
                View view = convertView;  
                /***
                 * 在这里尽可能每次都进行实例化新的,这样在拖拽ListView的时候不会出现错乱.
                 * 具体原因不明,不过这样经过测试,目前没有发现错乱。虽说效率不高,但是做拖拽LisView足够了。
                 */  
                view = LayoutInflater.from(context).inflate(  
                        R.layout.drag_list_item, null);  
      
                TextView textView = (TextView) view  
                        .findViewById(R.id.tv_drag_list_item_text);  
                ImageView imageView = (ImageView) view  
                        .findViewById(R.id.iv_drag_list_item_1);  
                imageView.setImageResource(arrayDrawables.get(position));  
                textView.setText(arrayTitles.get(position));  
                return view;  
            }  
      
            /***
             * 动态修改ListVIiw的方位.
             *  
             * @param start
             *            点击移动的position
             * @param down
             *            松开时候的position
             */  
            public void update(int start, int down) {  
                // 获取删除的东东.  
                String title = arrayTitles.get(start);  
                int drawable_id = arrayDrawables.get(start);  
      
                arrayTitles.remove(start);// 删除该项  
                arrayDrawables.remove(start);// 删除该项  
      
                arrayTitles.add(down, title);// 添加删除项  
                arrayDrawables.add(down, drawable_id);// 添加删除项  
      
                notifyDataSetChanged();// 刷新ListView  
            }  
      
            @Override  
            public int getCount() {  
      
                return Title.length;  
            }  
      
            @Override  
            public Object getItem(int position) {  
                return Title[position];  
            }  
      
            @Override  
            public long getItemId(int position) {  
                return position;  
            }  
      
        }  

这里不过多解释了,相信大家都看的明白.如果疑问请留言.

展示下运行效果:

效果看起来还行吧,如果觉得不错的话,记得要赞一个哦.

下面我们接着修改,模拟百度嘛,谁让百度这么牛叉呢.

思路:点中拖拉图标的时候,每次移动只要dragPosition发生改变,也就是我移动到了下一个位置,那么此时我就进行交换执行update.并且除了第一次移动外,在每次交换后要除去映射源的显示,这样用户觉得这里的空位就是就是为我准备的,比较人性化.

实现起来并不复杂,前提是你得掌握上面的操作.

源码如下;
[java] view plaincopy

    package com.jj.drag;  
      
    import android.content.Context;  
    import android.graphics.Bitmap;  
    import android.graphics.Color;  
    import android.os.AsyncTask;  
    import android.util.AttributeSet;  
    import android.util.Log;  
    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.AbsListView;  
    import android.widget.AbsListView.OnScrollListener;  
    import android.widget.AdapterView;  
    import android.widget.ImageView;  
    import android.widget.ListView;  
      
    import com.jj.drag.MainActivity.DragListAdapter;  
      
    public class DragListView extends ListView {  
      
        private WindowManager windowManager;// windows窗口控制类  
        private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
      
        private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
      
        private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
        private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
        private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
      
        private int dragPoint;// 在当前数据项中的位置  
        private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上)  
      
        private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
        private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
      
        private final static int step = 1;// ListView 滑动步伐.  
      
        private int current_Step;// 当前步伐.  
      
        private int temChangId;// 临时交换id  
      
        private boolean isLock;// 是否上锁.  
      
        public void setLock(boolean isLock) {  
            this.isLock = isLock;  
        }  
      
        public DragListView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
            scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
        }  
      
        /***
         * touch事件拦截 在这里我进行相应拦截,
         */  
        @Override  
        public boolean onInterceptTouchEvent(MotionEvent ev) {  
            // 按下  
            if (ev.getAction() == MotionEvent.ACTION_DOWN && !isLock) {  
                int x = (int) ev.getX();// 获取相对与ListView的x坐标  
                int y = (int) ev.getY();// 获取相应与ListView的y坐标  
                temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);  
                // 无效不进行处理  
                if (dragPosition == AdapterView.INVALID_POSITION) {  
                    return super.onInterceptTouchEvent(ev);  
                }  
      
                // 获取当前位置的视图(可见状态)  
                ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
                        - getFirstVisiblePosition());  
      
                // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
                dragPoint = y - itemView.getTop();  
                // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
                dragOffset = (int) (ev.getRawY() - y);  
      
                // 获取可拖拽的图标  
                View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
      
                // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
                if (dragger != null && x > dragger.getLeft() - 20) {  
      
                    upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
                    downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
                    itemView.setBackgroundColor(Color.BLUE);  
                    itemView.setDrawingCacheEnabled(true);// 开启cache.  
                    Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
                    startDrag(bm, y);// 初始化影像  
                }  
                return false;  
            }  
      
            return super.onInterceptTouchEvent(ev);  
        }  
      
        /**
         * 触摸事件处理
         */  
        @Override  
        public boolean onTouchEvent(MotionEvent ev) {  
            // item的view不为空,且获取的dragPosition有效  
            if (dragImageView != null && dragPosition != INVALID_POSITION  
                    && !isLock) {  
                int action = ev.getAction();  
                switch (action) {  
                case MotionEvent.ACTION_UP:  
                    int upY = (int) ev.getY();  
                    stopDrag();  
                    onDrop(upY);  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    int moveY = (int) ev.getY();  
                    onDrag(moveY);  
      
                    break;  
                case MotionEvent.ACTION_DOWN:  
                    break;  
                default:  
                    break;  
                }  
                return true;// 取消ListView滑动.  
            }  
      
            return super.onTouchEvent(ev);  
        }  
      
        /**
         * 准备拖动,初始化拖动项的图像
         *  
         * @param bm
         * @param y
         */  
        private void startDrag(Bitmap bm, int y) {  
            // stopDrag();  
            /***
             * 初始化window.
             */  
            windowParams = new WindowManager.LayoutParams();  
            windowParams.gravity = Gravity.TOP;  
            windowParams.x = 0;  
            windowParams.y = y - dragPoint + dragOffset;  
            windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
            windowParams.height = 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 imageView = new ImageView(getContext());  
            imageView.setImageBitmap(bm);  
            windowManager = (WindowManager) getContext().getSystemService("window");  
            windowManager.addView(imageView, windowParams);  
            dragImageView = imageView;  
      
        }  
      
        /**
         * 拖动执行,在Move方法中执行
         *  
         * @param y
         */  
        public void onDrag(int y) {  
            int drag_top = y - dragPoint;// 拖拽view的top值不能<0,否则则出界.  
            if (dragImageView != null && drag_top >= 0) {  
                windowParams.alpha = 0.5f;  
                windowParams.y = y - dragPoint + dragOffset;  
                windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动.  
            }  
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(0, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
            }  
      
            onChange(y);// 时时交换  
      
            doScroller(y);// listview移动.  
        }  
      
        /***
         * ListView的移动.
         * 要明白移动原理:当我移动到下端的时候,ListView向上滑动,当我移动到上端的时候,ListView要向下滑动。正好和实际的相反.
         *  
         */  
      
        public void doScroller(int y) {  
            // Log.e("jj", "y=" + y);  
            // Log.e("jj", "upScrollBounce=" + upScrollBounce);  
            // ListView需要下滑  
            if (y < upScrollBounce) {  
                current_Step = step + (upScrollBounce - y) / 10;// 时时步伐  
            }// ListView需要上滑  
            else if (y > downScrollBounce) {  
                current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐  
            } else {  
                current_Step = 0;  
            }  
      
            // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)  
            View view = getChildAt(dragPosition - getFirstVisiblePosition());  
            // 真正滚动的方法setSelectionFromTop()  
            setSelectionFromTop(dragPosition, view.getTop() + current_Step);  
      
        }  
      
        /**
         * 停止拖动,删除影像
         */  
        public void stopDrag() {  
            if (dragImageView != null) {  
                windowManager.removeView(dragImageView);  
                dragImageView = null;  
            }  
        }  
      
        /***
         * 拖动时时change
         */  
        private void onChange(int y) {  
            // 数据交换  
            if (dragPosition < getAdapter().getCount()) {  
                DragListAdapter adapter = (DragListAdapter) getAdapter();  
                adapter.isHidden = false;  
                if (dragPosition != temChangId) {  
                    adapter.update(temChangId, dragPosition);  
                    temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位.  
                }  
            }  
      
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(0, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
            }  
      
            // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)  
            if (y < getChildAt(0).getTop()) {  
                // 超出上边界  
                dragPosition = 0;  
                // 如果拖动超过最后一项的最下边那么就防止在最下边  
            } else if (y > getChildAt(getChildCount() - 1).getBottom()) {  
                // 超出下边界  
                dragPosition = getAdapter().getCount() - 1;  
            }  
      
        }  
      
        /**
         * 拖动放下的时候
         *  
         * @param y
         */  
        public void onDrop(int y) {  
            // 数据交换  
            if (dragPosition < getAdapter().getCount()) {  
                DragListAdapter adapter = (DragListAdapter) getAdapter();  
                adapter.isHidden = false;  
                adapter.notifyDataSetChanged();// 刷新.  
            }  
        }  
      
    }  



因为我们要时时交换位置,所以将原先的拖动方法onDrop方法移动到onChange中.具体的还是看源码吧.

另外的就是对适配器的修改,因为你要对特殊的item进行隐藏之类的操作,这些代码我就不写了,我会将案例上传网上,不懂的可以下载源码.

好了还是我们来观看下效果吧.

怎么样,这个效果看起来要比上面那个效果更人性化点吧,我的操作或许有点快,不信的话,你自己手机体验一下吧.

关于ListView拖拽就说到这里,如有不足请大家自己创新.

下面我们接着对GridView的拖拽简单说明.因为这些在项目中我们都会用到,所以既然做到就做全面点吧.好了大家接着往下看吧.

首先说明,原理一样,都是拖动映像,记录拖动位置,然后调用notifyDataSetChanged更新UI.

而GridView不同的是你要根据x,y值共同获取点击的position和移动至的position,而ListView因为不涉及x坐标.

嗯,最初的原始移动我就不给大家展示了,效果也不是很友好,我直接展示时时更新的那种方法.效果类是与上面那个时时更新ListView一样。

原理也一样.下面我们直接看代码吧.
[java] view plaincopy

    package com.jj.draggrid;  
      
    import java.util.logging.Handler;  
      
    import com.jj.draggrid.MainActivity.DragGridAdapter;  
      
    import android.content.Context;  
    import android.graphics.Bitmap;  
    import android.graphics.PixelFormat;  
    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.widget.AdapterView;  
    import android.widget.BaseAdapter;  
    import android.widget.GridView;  
    import android.widget.ImageView;  
    import android.widget.Toast;  
      
    /***
     * 自定义拖拽GridView
     *  
     * @author zhangjia
     *  
     */  
      
    public class DragGridView extends GridView {  
      
        private WindowManager windowManager;// windows窗口控制类  
        private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
      
        private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
      
        private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
        private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
        private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
      
        private int dragPointX;// 在当前数据项中的位置  
        private int dragPointY;// 在当前数据项中的位置  
        private int dragOffsetX;// 当前视图和屏幕的距离(这里只使用了x方向上)  
        private int dragOffsetY;// 当前视图和屏幕的距离(这里只使用了y方向上)  
      
        private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
        private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
      
        private int temChangId;// 临时交换id  
      
        private boolean isDoTouch = false;// touch是否可用  
      
        private boolean isHide = false;// 是否隐藏  
      
        private Handler handler;  
      
        public void setDoTouch(boolean isDoTouch) {  
            this.isDoTouch = isDoTouch;  
        }  
      
        public DragGridView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
        }  
      
        @Override  
        public boolean onInterceptTouchEvent(MotionEvent ev) {  
      
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
                int x = (int) ev.getX();  
                int y = (int) ev.getY();  
      
                temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);  
      
                if (dragPosition == AdapterView.INVALID_POSITION) {  
                    return super.onInterceptTouchEvent(ev);  
                }  
      
                ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
                        - getFirstVisiblePosition());  
      
                dragPointX = x - itemView.getLeft();  
                dragPointY = y - itemView.getTop();  
                dragOffsetX = (int) (ev.getRawX() - x);  
                dragOffsetY = (int) (ev.getRawY() - y);  
      
                View dragger = itemView.findViewById(R.id.drag_grid_item);  
      
                /***
                 * 判断是否选中拖动图标
                 */  
                if (dragger != null && dragPointX > dragger.getLeft()  
                        && dragPointX < dragger.getRight()  
                        && dragPointY > dragger.getTop()  
                        && dragPointY < dragger.getBottom() + 20) {  
      
                    upScrollBounce = getHeight() / 4;  
                    downScrollBounce = getHeight() * 3 / 4;  
      
                    itemView.setDrawingCacheEnabled(true);  
                    Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());  
                    startDrag(bm, x, y);// 初始话映像  
      
                    dragger.setVisibility(View.INVISIBLE);// 隐藏该项.  
                }  
            }  
      
            return super.onInterceptTouchEvent(ev);  
        }  
      
        @Override  
        public boolean onTouchEvent(MotionEvent ev) {  
      
            if (dragImageView != null && dragPosition != INVALID_POSITION  
                    && isDoTouch) {  
                int action = ev.getAction();  
                switch (action) {  
                /***
                 *  
                 */  
                case MotionEvent.ACTION_UP:  
                    int upX = (int) ev.getX();  
                    int upY = (int) ev.getY();  
                    stopDrag();// 删除映像  
                    onDrop(upX, upY);// 松开  
                    // isDoTouch = false;  
                    break;  
                /***
                 * 拖拽item
                 *  
                 */  
                case MotionEvent.ACTION_MOVE:  
                    int moveX = (int) ev.getX();  
                    int moveY = (int) ev.getY();  
                    onDrag(moveX, moveY);// 拖拽  
                    break;  
                case MotionEvent.ACTION_DOWN:  
                    int downX = (int) ev.getX();  
                    int downY = (int) ev.getY();  
                    onHide(downX, downY);// 隐藏该项  
                    break;  
                default:  
                    break;  
                }  
                return true;  
            }  
      
            return super.onTouchEvent(ev);  
        }  
      
        /**
         * 准备拖动,初始化拖动项的图像
         *  
         * @param bm
         * @param y
         */  
        public void startDrag(Bitmap bm, int x, int y) {  
      
            windowParams = new WindowManager.LayoutParams();  
            windowParams.gravity = Gravity.TOP | Gravity.LEFT;  
            windowParams.x = x - dragPointX + dragOffsetX;  
            windowParams.y = y - dragPointY + dragOffsetY;  
            windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
            windowParams.height = 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.windowAnimations = 0;  
      
            ImageView imageView = new ImageView(getContext());  
            imageView.setImageBitmap(bm);  
            windowManager = (WindowManager) getContext().getSystemService("window");  
            windowManager.addView(imageView, windowParams);  
            dragImageView = imageView;  
      
        }  
      
        /***
         * 拖动时时change
         */  
        private void onChange(int x, int y) {  
            // 获取适配器  
            DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
            // 数据交换  
            if (dragPosition < getAdapter().getCount()) {  
                // 不相等的情况下要进行换位,相等的情况下说明正在移动  
                if (dragPosition != temChangId) {  
                    adapter.update(temChangId, dragPosition);// 进行换位  
                    temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位.  
                }  
      
            }  
      
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(x, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
            }  
      
        }  
      
        /***
         * 拖动执行,在Move方法中执行
         *  
         * @param x
         * @param y
         */  
        public void onDrag(int x, int y) {  
            // 移动  
            if (dragImageView != null) {  
                windowParams.alpha = 0.8f;  
                windowParams.x = x - dragPointX + dragOffsetX;  
                windowParams.y = y - dragPointY + dragOffsetY;  
                windowManager.updateViewLayout(dragImageView, windowParams);  
            }  
      
            onChange(x, y);// 时时交换  
      
            // 滚动  
            if (y < upScrollBounce || y > downScrollBounce) {  
                // 使用setSelection来实现滚动  
                setSelection(dragPosition);  
            }  
      
        }  
      
        /***
         * 隐藏该选项
         */  
        private void onHide(int x, int y) {  
            // 获取适配器  
            DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
            // 为了避免滑动到分割线的时候,返回-1的问题  
            int tempPosition = pointToPosition(x, y);  
            if (tempPosition != INVALID_POSITION) {  
                dragPosition = tempPosition;  
            }  
            adapter.setIsHidePosition(dragPosition);  
      
        }  
      
        /**
         * 停止拖动,删除影像
         */  
        public void stopDrag() {  
            if (dragImageView != null) {  
                windowManager.removeView(dragImageView);  
                dragImageView = null;  
            }  
        }  
      
        /***
         * 拖动放下的时候
         *  
         * @param x
         * @param y
         */  
        public void onDrop(int x, int y) {  
      
            DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
            adapter.setIsHidePosition(-1);// 不进行隐藏  
      
        }  
      
    }  



相信大家只要ListView拖拽弄白后,这个GridView也会轻易弄出来,其实拖拽就是对坐标的考察。

向大家展示一下效果:

但是有个不足的地方,网上一些例子都是长按可以拖拽,而点击则执行点击事件.其实实现起来也不是很复杂,可是在实现的过程中,遇到了诡异纠结的问题,郁闷了一天,结果目前先放弃,以后哪天在搞搞吧.纠结的问题就是错位.

我说下我的思路:首先,我们在自定义GridView中创建一个控制是否可以Touch拖拽的变量,而这个变量的值我们通过对GridView的setOnItemClickListener和setOnItemLongClickListener来获取,

如:

[java] view plaincopy

    gv_main.setOnItemClickListener(new OnItemClickListener() {  
      
                @Override  
                public void onItemClick(AdapterView<?> parent, View view,  
                        int position, long id) {  
                    gv_main.setDoTouch(false);  
                    Toast.makeText(MainActivity.this,  
                            adapter.getItemId(position) + "", 1).show();  
                }  
            });  
      
            gv_main.setOnItemLongClickListener(new OnItemLongClickListener() {  
      
                @Override  
                public boolean onItemLongClick(AdapterView<?> parent, View view,  
                        int position, long id) {  
                    gv_main.setDoTouch(true);  
                    return true;  
                }  
            });  

这样我们就实现了长按可以拖拽的效果了,可是遇到个变态的问题,不过这个思路没有错,肯定可以实现.

就先说到这里,其实通过这个例子,我们还可以拓展实现ListView上滑动的时候,到达Title时,Title停留在顶部,当下一个Titile滑动到这里的时候,那么代替前面那个TItle.网上有写应该就是这么搞的,具体实现不知道,不过这种方案可以实现,有时间接着续.
0 0
原创粉丝点击