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

http://blog.csdn.net/jj120522/article/details/8240407#comments


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

   android ListView和GridView拖拽移位具体实现及拓展_第1张图片      android ListView和GridView拖拽移位具体实现及拓展_第2张图片      android ListView和GridView拖拽移位具体实现及拓展_第3张图片

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

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

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

onInterceptTouchEvent方法.

[java] view plain copy
  1. /*** 
  2.      * touch事件拦截  
  3.      */  
  4.     @Override  
  5.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  6.         // 按下  
  7.         if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
  8.             int x = (int) ev.getX();// 获取相对与ListView的x坐标  
  9.             int y = (int) ev.getY();// 获取相应与ListView的y坐标  
  10.             dragSrcPosition = dragPosition = pointToPosition(x, y);  
  11.             // 无效不进行处理  
  12.             if (dragPosition == AdapterView.INVALID_POSITION) {  
  13.                 return super.onInterceptTouchEvent(ev);  
  14.             }  
  15.   
  16.             // 获取当前位置的视图(可见状态)  
  17.             ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
  18.                     - getFirstVisiblePosition());  
  19.   
  20.             // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
  21.             dragPoint = y - itemView.getTop();  
  22.             // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
  23.             dragOffset = (int) (ev.getRawY() - y);  
  24.   
  25.             // 获取可拖拽的图标  
  26.             View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
  27.   
  28.             // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
  29.             if (dragger != null && x > dragger.getLeft() - 20) {  
  30.   
  31.                 upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
  32.                 downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
  33.   
  34.                 itemView.setDrawingCacheEnabled(true);// 开启cache.  
  35.                 Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
  36.                 startDrag(bm, y);// 初始化影像  
  37.             }  
  38.             // return false;  
  39.         }  
  40.   
  41.         return super.onInterceptTouchEvent(ev);  
  42.     }  

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

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

接下来我们看onTouchEvent事件:

[java] view plain copy
  1. /** 
  2.      * 触摸事件处理 
  3.      */  
  4.     @Override  
  5.     public boolean onTouchEvent(MotionEvent ev) {  
  6.         // item的view不为空,且获取的dragPosition有效  
  7.         if (dragImageView != null && dragPosition != INVALID_POSITION) {  
  8.             int action = ev.getAction();  
  9.             switch (action) {  
  10.             case MotionEvent.ACTION_UP:  
  11.                 int upY = (int) ev.getY();  
  12.                 stopDrag();  
  13.                 onDrop(upY);  
  14.                 break;  
  15.             case MotionEvent.ACTION_MOVE:  
  16.                 int moveY = (int) ev.getY();  
  17.                 onDrag(moveY);  
  18.   
  19.                 break;  
  20.             case MotionEvent.ACTION_DOWN:  
  21.                 break;  
  22.             default:  
  23.                 break;  
  24.             }  
  25.             return true;// 取消ListView滑动.  
  26.         }  
  27.   
  28.         return super.onTouchEvent(ev);  
  29.     }  
简单说明: 首先在Touch中,我们要进行判断,是否点击的是拖动图标,如果是的话,那么对ACTION_MOVE and ACTION_UP相应事件进行处理,并且返回true or false.作用:取消ListView自身的Touch事件.如果不是的话,执行ListView 本身的Touch事件.

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

对了,提醒大家要理解这三句话:

getRawX()和getRawY():获得的是相对屏幕的位置.
getX()和getY():获得的永远是相对view的触摸位置 坐标这两个值不会超过view的长度和宽度)。
getLeft getTopgetBottom,getRight这个指的是该控件相对于父控件的距离.
源码:

[java] view plain copy
  1. package com.jj.drag;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.os.AsyncTask;  
  6. import android.util.AttributeSet;  
  7. import android.util.Log;  
  8. import android.view.Gravity;  
  9. import android.view.MotionEvent;  
  10. import android.view.View;  
  11. import android.view.ViewConfiguration;  
  12. import android.view.ViewGroup;  
  13. import android.view.WindowManager;  
  14. import android.widget.AbsListView;  
  15. import android.widget.AbsListView.OnScrollListener;  
  16. import android.widget.AdapterView;  
  17. import android.widget.ImageView;  
  18. import android.widget.ListView;  
  19.   
  20. import com.jj.drag.MainActivity.DragListAdapter;  
  21.   
  22. /*** 
  23.  * 自定义拖拽ListView 
  24.  *  
  25.  * @author zhangjia 
  26.  *  
  27.  */  
  28. public class DragListView extends ListView {  
  29.   
  30.     private WindowManager windowManager;// windows窗口控制类  
  31.     private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
  32.   
  33.     private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
  34.   
  35.     private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
  36.     private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
  37.     private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
  38.   
  39.     private int dragPoint;// 在当前数据项中的位置  
  40.     private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上)  
  41.   
  42.     private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
  43.     private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
  44.   
  45.     private final static int step = 1;// ListView 滑动步伐.  
  46.   
  47.     private int current_Step;// 当前步伐.  
  48.   
  49.     /*** 
  50.      * 构造方法 
  51.      *  
  52.      * @param context 
  53.      * @param attrs 
  54.      */  
  55.     public DragListView(Context context, AttributeSet attrs) {  
  56.         super(context, attrs);  
  57.     }  
  58.   
  59.     /*** 
  60.      * touch事件拦截 
  61.      */  
  62.     @Override  
  63.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  64.         // 按下  
  65.         if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
  66.             int x = (int) ev.getX();// 获取相对与ListView的x坐标  
  67.             int y = (int) ev.getY();// 获取相应与ListView的y坐标  
  68.             dragSrcPosition = dragPosition = pointToPosition(x, y);  
  69.             // 无效不进行处理  
  70.             if (dragPosition == AdapterView.INVALID_POSITION) {  
  71.                 return super.onInterceptTouchEvent(ev);  
  72.             }  
  73.   
  74.             // 获取当前位置的视图(可见状态)  
  75.             ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
  76.                     - getFirstVisiblePosition());  
  77.   
  78.             // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
  79.             dragPoint = y - itemView.getTop();  
  80.             // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
  81.             dragOffset = (int) (ev.getRawY() - y);  
  82.   
  83.             // 获取可拖拽的图标  
  84.             View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
  85.   
  86.             // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
  87.             if (dragger != null && x > dragger.getLeft() - 20) {  
  88.   
  89.                 upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
  90.                 downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
  91.   
  92.                 itemView.setDrawingCacheEnabled(true);// 开启cache.  
  93.                 Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
  94.                 startDrag(bm, y);// 初始化影像  
  95.             }  
  96.         }  
  97.   
  98.         return super.onInterceptTouchEvent(ev);  
  99.     }  
  100.   
  101.     /** 
  102.      * 触摸事件处理 
  103.      */  
  104.     @Override  
  105.     public boolean onTouchEvent(MotionEvent ev) {  
  106.         // item的view不为空,且获取的dragPosition有效  
  107.         if (dragImageView != null && dragPosition != INVALID_POSITION) {  
  108.             int action = ev.getAction();  
  109.             switch (action) {  
  110.             case MotionEvent.ACTION_UP:  
  111.                 int upY = (int) ev.getY();  
  112.                 stopDrag();  
  113.                 onDrop(upY);  
  114.                 break;  
  115.             case MotionEvent.ACTION_MOVE:  
  116.                 int moveY = (int) ev.getY();  
  117.                 onDrag(moveY);  
  118.                 break;  
  119.             case MotionEvent.ACTION_DOWN:  
  120.                 break;  
  121.             default:  
  122.                 break;  
  123.             }  
  124.             return true;// 取消ListView滑动.  
  125.         }  
  126.   
  127.         return super.onTouchEvent(ev);  
  128.     }  
  129.   
  130.     /** 
  131.      * 准备拖动,初始化拖动项的图像 
  132.      *  
  133.      * @param bm 
  134.      * @param y 
  135.      */  
  136.     private void startDrag(Bitmap bm, int y) {  
  137.         // stopDrag();  
  138.         /*** 
  139.          * 初始化window. 
  140.          */  
  141.         windowParams = new WindowManager.LayoutParams();  
  142.         windowParams.gravity = Gravity.TOP;  
  143.         windowParams.x = 0;  
  144.         windowParams.y = y - dragPoint + dragOffset;  
  145.         windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  146.         windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  147.   
  148.         windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 不需获取焦点  
  149.                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不需接受触摸事件  
  150.                 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON// 保持设备常开,并保持亮度不变。  
  151.                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;// 窗口占满整个屏幕,忽略周围的装饰边框(例如状态栏)。此窗口需考虑到装饰边框的内容。  
  152.   
  153.         // windowParams.format = PixelFormat.TRANSLUCENT;// 默认为不透明,这里设成透明效果.  
  154.         windowParams.windowAnimations = 0;// 窗口所使用的动画设置  
  155.   
  156.         ImageView imageView = new ImageView(getContext());  
  157.         imageView.setImageBitmap(bm);  
  158.         windowManager = (WindowManager) getContext().getSystemService("window");  
  159.         windowManager.addView(imageView, windowParams);  
  160.         dragImageView = imageView;  
  161.   
  162.     }  
  163.   
  164.     /** 
  165.      * 拖动执行,在Move方法中执行 
  166.      *  
  167.      * @param y 
  168.      */  
  169.     public void onDrag(int y) {  
  170.         int drag_top = y - dragPoint;// 拖拽view的top值不能<0,否则则出界.  
  171.         if (dragImageView != null && drag_top >= 0) {  
  172.             windowParams.alpha = 0.5f;// 透明度  
  173.             windowParams.y = y - dragPoint + dragOffset;// 移动y值.//记得要加上dragOffset,windowManager计算的是整个屏幕.(标题栏和状态栏都要算上)  
  174.             windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动.  
  175.         }  
  176.         // 为了避免滑动到分割线的时候,返回-1的问题  
  177.         int tempPosition = pointToPosition(0, y);  
  178.         if (tempPosition != INVALID_POSITION) {  
  179.             dragPosition = tempPosition;  
  180.   
  181.         }  
  182.         doScroller(y);  
  183.     }  
  184.   
  185.     /*** 
  186.      * ListView的移动. 
  187.      * 要明白移动原理:当映像移动到下端的时候,ListView向上滑动,当映像移动到上端的时候,ListView要向下滑动。正好和实际的相反. 
  188.      *  
  189.      */  
  190.   
  191.     public void doScroller(int y) {  
  192.         Log.e("jj""y=" + y);  
  193.         Log.e("jj""upScrollBounce=" + upScrollBounce);  
  194.         // ListView需要下滑  
  195.         if (y < upScrollBounce) {  
  196.             current_Step = step + (upScrollBounce - y) / 10;// 时时步伐  
  197.         }// ListView需要上滑  
  198.         else if (y > downScrollBounce) {  
  199.             current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐  
  200.         } else {  
  201.             current_Step = 0;  
  202.         }  
  203.   
  204.         // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)  
  205.         View view = getChildAt(dragPosition - getFirstVisiblePosition());  
  206.         // 真正滚动的方法setSelectionFromTop()  
  207.         setSelectionFromTop(dragPosition, view.getTop() + current_Step);  
  208.   
  209.     }  
  210.   
  211.     /** 
  212.      * 停止拖动,删除影像 
  213.      */  
  214.     public void stopDrag() {  
  215.         if (dragImageView != null) {  
  216.             windowManager.removeView(dragImageView);  
  217.             dragImageView = null;  
  218.         }  
  219.     }  
  220.   
  221.     /** 
  222.      * 拖动放下的时候 
  223.      *  
  224.      * @param y 
  225.      */  
  226.     public void onDrop(int y) {  
  227.   
  228.         // 为了避免滑动到分割线的时候,返回-1的问题  
  229.         int tempPosition = pointToPosition(0, y);  
  230.         if (tempPosition != INVALID_POSITION) {  
  231.             dragPosition = tempPosition;  
  232.         }  
  233.   
  234.         // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)  
  235.         if (y < getChildAt(0).getTop()) {  
  236.             // 超出上边界  
  237.             dragPosition = 0;  
  238.             // 如果拖动超过最后一项的最下边那么就防止在最下边  
  239.         } else if (y > getChildAt(getChildCount() - 1).getBottom()) {  
  240.             // 超出下边界  
  241.             dragPosition = getAdapter().getCount() - 1;  
  242.         }  
  243.   
  244.         // 数据交换  
  245.         if (dragPosition < getAdapter().getCount()) {  
  246.             DragListAdapter adapter = (DragListAdapter) getAdapter();  
  247.             adapter.update(dragSrcPosition, dragPosition);  
  248.         }  
  249.   
  250.     }  
  251.   
  252. }  
下面我说下适配器:

[java] view plain copy
  1. /*** 
  2.      * 自定义适配器 
  3.      *  
  4.      * @author zhangjia 
  5.      *  
  6.      */  
  7.     class DragListAdapter extends BaseAdapter {  
  8.         private ArrayList<String> arrayTitles;  
  9.         private ArrayList<Integer> arrayDrawables;  
  10.         private Context context;  
  11.   
  12.         public DragListAdapter(Context context, ArrayList<String> arrayTitles,  
  13.                 ArrayList<Integer> arrayDrawables) {  
  14.             this.context = context;  
  15.             this.arrayTitles = arrayTitles;  
  16.             this.arrayDrawables = arrayDrawables;  
  17.         }  
  18.   
  19.         @Override  
  20.         public View getView(int position, View convertView, ViewGroup parent) {  
  21.             View view = convertView;  
  22.             /*** 
  23.              * 在这里尽可能每次都进行实例化新的,这样在拖拽ListView的时候不会出现错乱. 
  24.              * 具体原因不明,不过这样经过测试,目前没有发现错乱。虽说效率不高,但是做拖拽LisView足够了。 
  25.              */  
  26.             view = LayoutInflater.from(context).inflate(  
  27.                     R.layout.drag_list_item, null);  
  28.   
  29.             TextView textView = (TextView) view  
  30.                     .findViewById(R.id.tv_drag_list_item_text);  
  31.             ImageView imageView = (ImageView) view  
  32.                     .findViewById(R.id.iv_drag_list_item_1);  
  33.             imageView.setImageResource(arrayDrawables.get(position));  
  34.             textView.setText(arrayTitles.get(position));  
  35.             return view;  
  36.         }  
  37.   
  38.         /*** 
  39.          * 动态修改ListVIiw的方位. 
  40.          *  
  41.          * @param start 
  42.          *            点击移动的position 
  43.          * @param down 
  44.          *            松开时候的position 
  45.          */  
  46.         public void update(int start, int down) {  
  47.             // 获取删除的东东.  
  48.             String title = arrayTitles.get(start);  
  49.             int drawable_id = arrayDrawables.get(start);  
  50.   
  51.             arrayTitles.remove(start);// 删除该项  
  52.             arrayDrawables.remove(start);// 删除该项  
  53.   
  54.             arrayTitles.add(down, title);// 添加删除项  
  55.             arrayDrawables.add(down, drawable_id);// 添加删除项  
  56.   
  57.             notifyDataSetChanged();// 刷新ListView  
  58.         }  
  59.   
  60.         @Override  
  61.         public int getCount() {  
  62.   
  63.             return Title.length;  
  64.         }  
  65.   
  66.         @Override  
  67.         public Object getItem(int position) {  
  68.             return Title[position];  
  69.         }  
  70.   
  71.         @Override  
  72.         public long getItemId(int position) {  
  73.             return position;  
  74.         }  
  75.   
  76.     }  

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

展示下运行效果:

android ListView和GridView拖拽移位具体实现及拓展_第4张图片

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


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

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

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

源码如下;

[java] view plain copy
  1. package com.jj.drag;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.Color;  
  6. import android.os.AsyncTask;  
  7. import android.util.AttributeSet;  
  8. import android.util.Log;  
  9. import android.view.Gravity;  
  10. import android.view.MotionEvent;  
  11. import android.view.View;  
  12. import android.view.ViewConfiguration;  
  13. import android.view.ViewGroup;  
  14. import android.view.WindowManager;  
  15. import android.widget.AbsListView;  
  16. import android.widget.AbsListView.OnScrollListener;  
  17. import android.widget.AdapterView;  
  18. import android.widget.ImageView;  
  19. import android.widget.ListView;  
  20.   
  21. import com.jj.drag.MainActivity.DragListAdapter;  
  22.   
  23. public class DragListView extends ListView {  
  24.   
  25.     private WindowManager windowManager;// windows窗口控制类  
  26.     private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
  27.   
  28.     private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
  29.   
  30.     private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
  31.     private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
  32.     private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
  33.   
  34.     private int dragPoint;// 在当前数据项中的位置  
  35.     private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上)  
  36.   
  37.     private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
  38.     private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
  39.   
  40.     private final static int step = 1;// ListView 滑动步伐.  
  41.   
  42.     private int current_Step;// 当前步伐.  
  43.   
  44.     private int temChangId;// 临时交换id  
  45.   
  46.     private boolean isLock;// 是否上锁.  
  47.   
  48.     public void setLock(boolean isLock) {  
  49.         this.isLock = isLock;  
  50.     }  
  51.   
  52.     public DragListView(Context context, AttributeSet attrs) {  
  53.         super(context, attrs);  
  54.         scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
  55.     }  
  56.   
  57.     /*** 
  58.      * touch事件拦截 在这里我进行相应拦截, 
  59.      */  
  60.     @Override  
  61.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  62.         // 按下  
  63.         if (ev.getAction() == MotionEvent.ACTION_DOWN && !isLock) {  
  64.             int x = (int) ev.getX();// 获取相对与ListView的x坐标  
  65.             int y = (int) ev.getY();// 获取相应与ListView的y坐标  
  66.             temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);  
  67.             // 无效不进行处理  
  68.             if (dragPosition == AdapterView.INVALID_POSITION) {  
  69.                 return super.onInterceptTouchEvent(ev);  
  70.             }  
  71.   
  72.             // 获取当前位置的视图(可见状态)  
  73.             ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
  74.                     - getFirstVisiblePosition());  
  75.   
  76.             // 获取到的dragPoint其实就是在你点击指定item项中的高度.  
  77.             dragPoint = y - itemView.getTop();  
  78.             // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).  
  79.             dragOffset = (int) (ev.getRawY() - y);  
  80.   
  81.             // 获取可拖拽的图标  
  82.             View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);  
  83.   
  84.             // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)  
  85.             if (dragger != null && x > dragger.getLeft() - 20) {  
  86.   
  87.                 upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3  
  88.                 downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3  
  89.                 itemView.setBackgroundColor(Color.BLUE);  
  90.                 itemView.setDrawingCacheEnabled(true);// 开启cache.  
  91.                 Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.  
  92.                 startDrag(bm, y);// 初始化影像  
  93.             }  
  94.             return false;  
  95.         }  
  96.   
  97.         return super.onInterceptTouchEvent(ev);  
  98.     }  
  99.   
  100.     /** 
  101.      * 触摸事件处理 
  102.      */  
  103.     @Override  
  104.     public boolean onTouchEvent(MotionEvent ev) {  
  105.         // item的view不为空,且获取的dragPosition有效  
  106.         if (dragImageView != null && dragPosition != INVALID_POSITION  
  107.                 && !isLock) {  
  108.             int action = ev.getAction();  
  109.             switch (action) {  
  110.             case MotionEvent.ACTION_UP:  
  111.                 int upY = (int) ev.getY();  
  112.                 stopDrag();  
  113.                 onDrop(upY);  
  114.                 break;  
  115.             case MotionEvent.ACTION_MOVE:  
  116.                 int moveY = (int) ev.getY();  
  117.                 onDrag(moveY);  
  118.   
  119.                 break;  
  120.             case MotionEvent.ACTION_DOWN:  
  121.                 break;  
  122.             default:  
  123.                 break;  
  124.             }  
  125.             return true;// 取消ListView滑动.  
  126.         }  
  127.   
  128.         return super.onTouchEvent(ev);  
  129.     }  
  130.   
  131.     /** 
  132.      * 准备拖动,初始化拖动项的图像 
  133.      *  
  134.      * @param bm 
  135.      * @param y 
  136.      */  
  137.     private void startDrag(Bitmap bm, int y) {  
  138.         // stopDrag();  
  139.         /*** 
  140.          * 初始化window. 
  141.          */  
  142.         windowParams = new WindowManager.LayoutParams();  
  143.         windowParams.gravity = Gravity.TOP;  
  144.         windowParams.x = 0;  
  145.         windowParams.y = y - dragPoint + dragOffset;  
  146.         windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  147.         windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  148.   
  149.         windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 不需获取焦点  
  150.                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不需接受触摸事件  
  151.                 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON// 保持设备常开,并保持亮度不变。  
  152.                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;// 窗口占满整个屏幕,忽略周围的装饰边框(例如状态栏)。此窗口需考虑到装饰边框的内容。  
  153.   
  154.         // windowParams.format = PixelFormat.TRANSLUCENT;// 默认为不透明,这里设成透明效果.  
  155.         windowParams.windowAnimations = 0;// 窗口所使用的动画设置  
  156.   
  157.         ImageView imageView = new ImageView(getContext());  
  158.         imageView.setImageBitmap(bm);  
  159.         windowManager = (WindowManager) getContext().getSystemService("window");  
  160.         windowManager.addView(imageView, windowParams);  
  161.         dragImageView = imageView;  
  162.   
  163.     }  
  164.   
  165.     /** 
  166.      * 拖动执行,在Move方法中执行 
  167.      *  
  168.      * @param y 
  169.      */  
  170.     public void onDrag(int y) {  
  171.         int drag_top = y - dragPoint;// 拖拽view的top值不能<0,否则则出界.  
  172.         if (dragImageView != null && drag_top >= 0) {  
  173.             windowParams.alpha = 0.5f;  
  174.             windowParams.y = y - dragPoint + dragOffset;  
  175.             windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动.  
  176.         }  
  177.         // 为了避免滑动到分割线的时候,返回-1的问题  
  178.         int tempPosition = pointToPosition(0, y);  
  179.         if (tempPosition != INVALID_POSITION) {  
  180.             dragPosition = tempPosition;  
  181.         }  
  182.   
  183.         onChange(y);// 时时交换  
  184.   
  185.         doScroller(y);// listview移动.  
  186.     }  
  187.   
  188.     /*** 
  189.      * ListView的移动. 
  190.      * 要明白移动原理:当我移动到下端的时候,ListView向上滑动,当我移动到上端的时候,ListView要向下滑动。正好和实际的相反. 
  191.      *  
  192.      */  
  193.   
  194.     public void doScroller(int y) {  
  195.         // Log.e("jj", "y=" + y);  
  196.         // Log.e("jj", "upScrollBounce=" + upScrollBounce);  
  197.         // ListView需要下滑  
  198.         if (y < upScrollBounce) {  
  199.             current_Step = step + (upScrollBounce - y) / 10;// 时时步伐  
  200.         }// ListView需要上滑  
  201.         else if (y > downScrollBounce) {  
  202.             current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐  
  203.         } else {  
  204.             current_Step = 0;  
  205.         }  
  206.   
  207.         // 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position)  
  208.         View view = getChildAt(dragPosition - getFirstVisiblePosition());  
  209.         // 真正滚动的方法setSelectionFromTop()  
  210.         setSelectionFromTop(dragPosition, view.getTop() + current_Step);  
  211.   
  212.     }  
  213.   
  214.     /** 
  215.      * 停止拖动,删除影像 
  216.      */  
  217.     public void stopDrag() {  
  218.         if (dragImageView != null) {  
  219.             windowManager.removeView(dragImageView);  
  220.             dragImageView = null;  
  221.         }  
  222.     }  
  223.   
  224.     /*** 
  225.      * 拖动时时change 
  226.      */  
  227.     private void onChange(int y) {  
  228.         // 数据交换  
  229.         if (dragPosition < getAdapter().getCount()) {  
  230.             DragListAdapter adapter = (DragListAdapter) getAdapter();  
  231.             adapter.isHidden = false;  
  232.             if (dragPosition != temChangId) {  
  233.                 adapter.update(temChangId, dragPosition);  
  234.                 temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位.  
  235.             }  
  236.         }  
  237.   
  238.         // 为了避免滑动到分割线的时候,返回-1的问题  
  239.         int tempPosition = pointToPosition(0, y);  
  240.         if (tempPosition != INVALID_POSITION) {  
  241.             dragPosition = tempPosition;  
  242.         }  
  243.   
  244.         // 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置)  
  245.         if (y < getChildAt(0).getTop()) {  
  246.             // 超出上边界  
  247.             dragPosition = 0;  
  248.             // 如果拖动超过最后一项的最下边那么就防止在最下边  
  249.         } else if (y > getChildAt(getChildCount() - 1).getBottom()) {  
  250.             // 超出下边界  
  251.             dragPosition = getAdapter().getCount() - 1;  
  252.         }  
  253.   
  254.     }  
  255.   
  256.     /** 
  257.      * 拖动放下的时候 
  258.      *  
  259.      * @param y 
  260.      */  
  261.     public void onDrop(int y) {  
  262.         // 数据交换  
  263.         if (dragPosition < getAdapter().getCount()) {  
  264.             DragListAdapter adapter = (DragListAdapter) getAdapter();  
  265.             adapter.isHidden = false;  
  266.             adapter.notifyDataSetChanged();// 刷新.  
  267.         }  
  268.     }  
  269.   
  270. }  


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

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

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

android ListView和GridView拖拽移位具体实现及拓展_第5张图片

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

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


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

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

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

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

原理也一样.下面我们直接看代码吧.

[java] view plain copy
  1. package com.jj.draggrid;  
  2.   
  3. import java.util.logging.Handler;  
  4.   
  5. import com.jj.draggrid.MainActivity.DragGridAdapter;  
  6.   
  7. import android.content.Context;  
  8. import android.graphics.Bitmap;  
  9. import android.graphics.PixelFormat;  
  10. import android.util.AttributeSet;  
  11. import android.util.Log;  
  12. import android.view.Gravity;  
  13. import android.view.MotionEvent;  
  14. import android.view.View;  
  15. import android.view.ViewGroup;  
  16. import android.view.WindowManager;  
  17. import android.widget.AdapterView;  
  18. import android.widget.BaseAdapter;  
  19. import android.widget.GridView;  
  20. import android.widget.ImageView;  
  21. import android.widget.Toast;  
  22.   
  23. /*** 
  24.  * 自定义拖拽GridView 
  25.  *  
  26.  * @author zhangjia 
  27.  *  
  28.  */  
  29.   
  30. public class DragGridView extends GridView {  
  31.   
  32.     private WindowManager windowManager;// windows窗口控制类  
  33.     private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数  
  34.   
  35.     private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24)  
  36.   
  37.     private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView  
  38.     private int dragSrcPosition;// 手指拖动项原始在列表中的位置  
  39.     private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置.  
  40.   
  41.     private int dragPointX;// 在当前数据项中的位置  
  42.     private int dragPointY;// 在当前数据项中的位置  
  43.     private int dragOffsetX;// 当前视图和屏幕的距离(这里只使用了x方向上)  
  44.     private int dragOffsetY;// 当前视图和屏幕的距离(这里只使用了y方向上)  
  45.   
  46.     private int upScrollBounce;// 拖动的时候,开始向上滚动的边界  
  47.     private int downScrollBounce;// 拖动的时候,开始向下滚动的边界  
  48.   
  49.     private int temChangId;// 临时交换id  
  50.   
  51.     private boolean isDoTouch = false;// touch是否可用  
  52.   
  53.     private boolean isHide = false;// 是否隐藏  
  54.   
  55.     private Handler handler;  
  56.   
  57.     public void setDoTouch(boolean isDoTouch) {  
  58.         this.isDoTouch = isDoTouch;  
  59.     }  
  60.   
  61.     public DragGridView(Context context, AttributeSet attrs) {  
  62.         super(context, attrs);  
  63.     }  
  64.   
  65.     @Override  
  66.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  67.   
  68.         if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
  69.             int x = (int) ev.getX();  
  70.             int y = (int) ev.getY();  
  71.   
  72.             temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y);  
  73.   
  74.             if (dragPosition == AdapterView.INVALID_POSITION) {  
  75.                 return super.onInterceptTouchEvent(ev);  
  76.             }  
  77.   
  78.             ViewGroup itemView = (ViewGroup) getChildAt(dragPosition  
  79.                     - getFirstVisiblePosition());  
  80.   
  81.             dragPointX = x - itemView.getLeft();  
  82.             dragPointY = y - itemView.getTop();  
  83.             dragOffsetX = (int) (ev.getRawX() - x);  
  84.             dragOffsetY = (int) (ev.getRawY() - y);  
  85.   
  86.             View dragger = itemView.findViewById(R.id.drag_grid_item);  
  87.   
  88.             /*** 
  89.              * 判断是否选中拖动图标 
  90.              */  
  91.             if (dragger != null && dragPointX > dragger.getLeft()  
  92.                     && dragPointX < dragger.getRight()  
  93.                     && dragPointY > dragger.getTop()  
  94.                     && dragPointY < dragger.getBottom() + 20) {  
  95.   
  96.                 upScrollBounce = getHeight() / 4;  
  97.                 downScrollBounce = getHeight() * 3 / 4;  
  98.   
  99.                 itemView.setDrawingCacheEnabled(true);  
  100.                 Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());  
  101.                 startDrag(bm, x, y);// 初始话映像  
  102.   
  103.                 dragger.setVisibility(View.INVISIBLE);// 隐藏该项.  
  104.             }  
  105.         }  
  106.   
  107.         return super.onInterceptTouchEvent(ev);  
  108.     }  
  109.   
  110.     @Override  
  111.     public boolean onTouchEvent(MotionEvent ev) {  
  112.   
  113.         if (dragImageView != null && dragPosition != INVALID_POSITION  
  114.                 && isDoTouch) {  
  115.             int action = ev.getAction();  
  116.             switch (action) {  
  117.             /*** 
  118.              *  
  119.              */  
  120.             case MotionEvent.ACTION_UP:  
  121.                 int upX = (int) ev.getX();  
  122.                 int upY = (int) ev.getY();  
  123.                 stopDrag();// 删除映像  
  124.                 onDrop(upX, upY);// 松开  
  125.                 // isDoTouch = false;  
  126.                 break;  
  127.             /*** 
  128.              * 拖拽item 
  129.              *  
  130.              */  
  131.             case MotionEvent.ACTION_MOVE:  
  132.                 int moveX = (int) ev.getX();  
  133.                 int moveY = (int) ev.getY();  
  134.                 onDrag(moveX, moveY);// 拖拽  
  135.                 break;  
  136.             case MotionEvent.ACTION_DOWN:  
  137.                 int downX = (int) ev.getX();  
  138.                 int downY = (int) ev.getY();  
  139.                 onHide(downX, downY);// 隐藏该项  
  140.                 break;  
  141.             default:  
  142.                 break;  
  143.             }  
  144.             return true;  
  145.         }  
  146.   
  147.         return super.onTouchEvent(ev);  
  148.     }  
  149.   
  150.     /** 
  151.      * 准备拖动,初始化拖动项的图像 
  152.      *  
  153.      * @param bm 
  154.      * @param y 
  155.      */  
  156.     public void startDrag(Bitmap bm, int x, int y) {  
  157.   
  158.         windowParams = new WindowManager.LayoutParams();  
  159.         windowParams.gravity = Gravity.TOP | Gravity.LEFT;  
  160.         windowParams.x = x - dragPointX + dragOffsetX;  
  161.         windowParams.y = y - dragPointY + dragOffsetY;  
  162.         windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  163.         windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  164.         windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
  165.                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE  
  166.                 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
  167.                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;  
  168.   
  169.         windowParams.windowAnimations = 0;  
  170.   
  171.         ImageView imageView = new ImageView(getContext());  
  172.         imageView.setImageBitmap(bm);  
  173.         windowManager = (WindowManager) getContext().getSystemService("window");  
  174.         windowManager.addView(imageView, windowParams);  
  175.         dragImageView = imageView;  
  176.   
  177.     }  
  178.   
  179.     /*** 
  180.      * 拖动时时change 
  181.      */  
  182.     private void onChange(int x, int y) {  
  183.         // 获取适配器  
  184.         DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
  185.         // 数据交换  
  186.         if (dragPosition < getAdapter().getCount()) {  
  187.             // 不相等的情况下要进行换位,相等的情况下说明正在移动  
  188.             if (dragPosition != temChangId) {  
  189.                 adapter.update(temChangId, dragPosition);// 进行换位  
  190.                 temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位.  
  191.             }  
  192.   
  193.         }  
  194.   
  195.         // 为了避免滑动到分割线的时候,返回-1的问题  
  196.         int tempPosition = pointToPosition(x, y);  
  197.         if (tempPosition != INVALID_POSITION) {  
  198.             dragPosition = tempPosition;  
  199.         }  
  200.   
  201.     }  
  202.   
  203.     /*** 
  204.      * 拖动执行,在Move方法中执行 
  205.      *  
  206.      * @param x 
  207.      * @param y 
  208.      */  
  209.     public void onDrag(int x, int y) {  
  210.         // 移动  
  211.         if (dragImageView != null) {  
  212.             windowParams.alpha = 0.8f;  
  213.             windowParams.x = x - dragPointX + dragOffsetX;  
  214.             windowParams.y = y - dragPointY + dragOffsetY;  
  215.             windowManager.updateViewLayout(dragImageView, windowParams);  
  216.         }  
  217.   
  218.         onChange(x, y);// 时时交换  
  219.   
  220.         // 滚动  
  221.         if (y < upScrollBounce || y > downScrollBounce) {  
  222.             // 使用setSelection来实现滚动  
  223.             setSelection(dragPosition);  
  224.         }  
  225.   
  226.     }  
  227.   
  228.     /*** 
  229.      * 隐藏该选项 
  230.      */  
  231.     private void onHide(int x, int y) {  
  232.         // 获取适配器  
  233.         DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
  234.         // 为了避免滑动到分割线的时候,返回-1的问题  
  235.         int tempPosition = pointToPosition(x, y);  
  236.         if (tempPosition != INVALID_POSITION) {  
  237.             dragPosition = tempPosition;  
  238.         }  
  239.         adapter.setIsHidePosition(dragPosition);  
  240.   
  241.     }  
  242.   
  243.     /** 
  244.      * 停止拖动,删除影像 
  245.      */  
  246.     public void stopDrag() {  
  247.         if (dragImageView != null) {  
  248.             windowManager.removeView(dragImageView);  
  249.             dragImageView = null;  
  250.         }  
  251.     }  
  252.   
  253.     /*** 
  254.      * 拖动放下的时候 
  255.      *  
  256.      * @param x 
  257.      * @param y 
  258.      */  
  259.     public void onDrop(int x, int y) {  
  260.   
  261.         DragGridAdapter adapter = (DragGridAdapter) getAdapter();  
  262.         adapter.setIsHidePosition(-1);// 不进行隐藏  
  263.   
  264.     }  
  265.   
  266. }  


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

向大家展示一下效果:

android ListView和GridView拖拽移位具体实现及拓展_第6张图片

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

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

如:

[java] view plain copy
  1. gv_main.setOnItemClickListener(new OnItemClickListener() {  
  2.   
  3.             @Override  
  4.             public void onItemClick(AdapterView<?> parent, View view,  
  5.                     int position, long id) {  
  6.                 gv_main.setDoTouch(false);  
  7.                 Toast.makeText(MainActivity.this,  
  8.                         adapter.getItemId(position) + ""1).show();  
  9.             }  
  10.         });  
  11.   
  12.         gv_main.setOnItemLongClickListener(new OnItemLongClickListener() {  
  13.   
  14.             @Override  
  15.             public boolean onItemLongClick(AdapterView<?> parent, View view,  
  16.                     int position, long id) {  
  17.                 gv_main.setDoTouch(true);  
  18.                 return true;  
  19.             }  
  20.         });  
这样我们就实现了长按可以拖拽的效果了,可是遇到个变态的问题,不过这个思路没有错,肯定可以实现.

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


你可能感兴趣的:(android ListView和GridView拖拽移位具体实现及拓展)