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

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

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

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

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

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

onInterceptTouchEvent方法.

  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. ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
  17. - getFirstVisiblePosition());
  18. // 获取到的dragPoint其实就是在你点击指定item项中的高度.
  19. dragPoint = y - itemView.getTop();
  20. // 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏).
  21. dragOffset = (int) (ev.getRawY() - y);
  22. // 获取可拖拽的图标
  23. View dragger = itemView.findViewById(R.id.iv_drag_list_item_2);
  24. // x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略)
  25. if (dragger != null && x > dragger.getLeft() - 20) {
  26. upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3
  27. downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3
  28. itemView.setDrawingCacheEnabled(true);// 开启cache.
  29. Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象.
  30. startDrag(bm, y);// 初始化影像
  31. }
  32. // return false;
  33. }
  34. return super.onInterceptTouchEvent(ev);
  35. }

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

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

接下来我们看onTouchEvent事件:

  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. break;
  19. case MotionEvent.ACTION_DOWN:
  20. break;
  21. default:
  22. break;
  23. }
  24. return true;// 取消ListView滑动.
  25. }
  26. return super.onTouchEvent(ev);
  27. }

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

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

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

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

下面我说下适配器:

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

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

展示下运行效果:

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

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

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

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

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

源码如下;

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

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

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

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

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

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

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


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

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

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

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

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

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

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

向大家展示一下效果:

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

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

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

如:

  1. gv_main.setOnItemClickListener(new OnItemClickListener() {
  2. @Override
  3. public void onItemClick(AdapterView<?> parent, View view,
  4. int position, long id) {
  5. gv_main.setDoTouch(false);
  6. Toast.makeText(MainActivity.this,
  7. adapter.getItemId(position) + "", 1).show();
  8. }
  9. });
  10. gv_main.setOnItemLongClickListener(new OnItemLongClickListener() {
  11. @Override
  12. public boolean onItemLongClick(AdapterView<?> parent, View view,
  13. int position, long id) {
  14. gv_main.setDoTouch(true);
  15. return true;
  16. }
  17. });

这样我们就实现了长按可以拖拽的效果了,可是遇到个变态的问题,不过这个思路没有错,肯定可以实现.就
先说到这里,其实通过这个例子,我们还可以拓展实现ListView上滑动的时候,到达Title时,Title停留在顶部,当下一个Titile滑动到
这里的时候,那么代替前面那个TItle.网上有写应该就是这么搞的,具体实现不知道,不过这种方案可以实现,有时间接着续.

上一篇:android ListView和GridView拖拽移位实现代码


下一篇:android ListView嵌套GridView显示不全问题