關於ListView拖拽移動位置,想必大家並不陌生,比較不錯的軟體都用到如此功能了.如:搜狐,網易,百度等,但是相比來說還是百度的用戶體驗較好,不偏心了,下面看幾個示例:

首先說一下:拖拽ListView的item就不應該可以任意移動,只應該在ListView所在的範圍內,而網易的你看看我都可以移動到狀態欄了,雖然你做了處理,但是用戶體驗我個人感覺不好,在看看百度的,不僅控制了移動範圍,更不錯的百度的移動起來會時時的換位,看起來相當的形象,所以我認為這樣相當的棒.

說明一點,我沒有那麼有才,我也是看別人代碼,然後自己整理下.在這裡就簡單記載一下.

首先對touch事件的處理,從應用中,我們可以得出,在我們點擊後面拖拉圖標後,就會創建一個item的影像視圖.並且可以移動該影像,而此時的ListView不應該有touch事件.

onInterceptTouchEvent方法.

[java] view plaincopyprint?
  1. /***
  2. *touch事件攔截
  3. */
  4. @Override
  5. publicbooleanonInterceptTouchEvent(MotionEventev){
  6. //按下
  7. if(ev.getAction()==MotionEvent.ACTION_DOWN){
  8. intx=(int)ev.getX();//獲取相對與ListView的x坐標
  9. inty=(int)ev.getY();//獲取相應與ListView的y坐標
  10. dragSrcPosition=dragPosition=pointToPosition(x,y);
  11. //無效不進行處理
  12. if(dragPosition==AdapterView.INVALID_POSITION){
  13. returnsuper.onInterceptTouchEvent(ev);
  14. }
  15. //獲取當前位置的視圖(可見狀態)
  16. ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
  17. -getFirstVisiblePosition());
  18. //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
  19. dragPoint=y-itemView.getTop();
  20. //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
  21. dragOffset=(int)(ev.getRawY()-y);
  22. //獲取可拖拽的圖標
  23. Viewdragger=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. Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
  30. startDrag(bm,y);//初始化影像
  31. }
  32. //returnfalse;
  33. }
  34. returnsuper.onInterceptTouchEvent(ev);
  35. }

/*** * 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 plaincopyprint?
  1. /**
  2. *觸摸事件處理
  3. */
  4. @Override
  5. publicbooleanonTouchEvent(MotionEventev){
  6. //item的view不為空,且獲取的dragPosition有效
  7. if(dragImageView!=null&&dragPosition!=INVALID_POSITION){
  8. intaction=ev.getAction();
  9. switch(action){
  10. caseMotionEvent.ACTION_UP:
  11. intupY=(int)ev.getY();
  12. stopDrag();
  13. onDrop(upY);
  14. break;
  15. caseMotionEvent.ACTION_MOVE:
  16. intmoveY=(int)ev.getY();
  17. onDrag(moveY);
  18. break;
  19. caseMotionEvent.ACTION_DOWN:
  20. break;
  21. default:
  22. break;
  23. }
  24. returntrue;//取消ListView滑動.
  25. }
  26. returnsuper.onTouchEvent(ev);
  27. }

/** * 觸摸事件處理 */ @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 andACTION_UP相應事件進行處理,並且返回true or false.作用:取消ListView自身的Touch事件.如果不是的話,執行ListView 本身的Touch事件.

大致就介紹這麼多,具體的實現,還是大家看源碼吧,我注釋的還算清晰,只要大家仔細看的話,一定可以掌握的,為什麼這麼說呢,技術只有在掌握了情況下才可以進行拓展.

對了,提醒大家要理解這三句話:

getRawX()和getRawY():獲得的是相對屏幕的位置.getX()和getY():獲得的永遠是相對view的觸摸位置 坐標(這兩個值不會超過view的長度和寬度)。getLeft,getTop,getBottom,getRight,這個指的是該控制項相對於父控制項的距離.源碼:[java] view plaincopyprint?
  1. packagecom.jj.drag;
  2. importandroid.content.Context;
  3. importandroid.graphics.Bitmap;
  4. importandroid.os.AsyncTask;
  5. importandroid.util.AttributeSet;
  6. importandroid.util.Log;
  7. importandroid.view.Gravity;
  8. importandroid.view.MotionEvent;
  9. importandroid.view.View;
  10. importandroid.view.ViewConfiguration;
  11. importandroid.view.ViewGroup;
  12. importandroid.view.WindowManager;
  13. importandroid.widget.AbsListView;
  14. importandroid.widget.AbsListView.OnScrollListener;
  15. importandroid.widget.AdapterView;
  16. importandroid.widget.ImageView;
  17. importandroid.widget.ListView;
  18. importcom.jj.drag.MainActivity.DragListAdapter;
  19. /***
  20. *自定義拖拽ListView
  21. *
  22. *@authorzhangjia
  23. *
  24. */
  25. publicclassDragListViewextendsListView{
  26. privateWindowManagerwindowManager;//windows窗口控制類
  27. privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
  28. privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
  29. privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
  30. privateintdragSrcPosition;//手指拖動項原始在列表中的位置
  31. privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
  32. privateintdragPoint;//在當前數據項中的位置
  33. privateintdragOffset;//當前視圖和屏幕的距離(這裡只使用了y方向上)
  34. privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
  35. privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
  36. privatefinalstaticintstep=1;//ListView滑動步伐.
  37. privateintcurrent_Step;//當前步伐.
  38. /***
  39. *構造方法
  40. *
  41. *@paramcontext
  42. *@paramattrs
  43. */
  44. publicDragListView(Contextcontext,AttributeSetattrs){
  45. super(context,attrs);
  46. }
  47. /***
  48. *touch事件攔截
  49. */
  50. @Override
  51. publicbooleanonInterceptTouchEvent(MotionEventev){
  52. //按下
  53. if(ev.getAction()==MotionEvent.ACTION_DOWN){
  54. intx=(int)ev.getX();//獲取相對與ListView的x坐標
  55. inty=(int)ev.getY();//獲取相應與ListView的y坐標
  56. dragSrcPosition=dragPosition=pointToPosition(x,y);
  57. //無效不進行處理
  58. if(dragPosition==AdapterView.INVALID_POSITION){
  59. returnsuper.onInterceptTouchEvent(ev);
  60. }
  61. //獲取當前位置的視圖(可見狀態)
  62. ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
  63. -getFirstVisiblePosition());
  64. //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
  65. dragPoint=y-itemView.getTop();
  66. //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
  67. dragOffset=(int)(ev.getRawY()-y);
  68. //獲取可拖拽的圖標
  69. Viewdragger=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. Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
  76. startDrag(bm,y);//初始化影像
  77. }
  78. }
  79. returnsuper.onInterceptTouchEvent(ev);
  80. }
  81. /**
  82. *觸摸事件處理
  83. */
  84. @Override
  85. publicbooleanonTouchEvent(MotionEventev){
  86. //item的view不為空,且獲取的dragPosition有效
  87. if(dragImageView!=null&&dragPosition!=INVALID_POSITION){
  88. intaction=ev.getAction();
  89. switch(action){
  90. caseMotionEvent.ACTION_UP:
  91. intupY=(int)ev.getY();
  92. stopDrag();
  93. onDrop(upY);
  94. break;
  95. caseMotionEvent.ACTION_MOVE:
  96. intmoveY=(int)ev.getY();
  97. onDrag(moveY);
  98. break;
  99. caseMotionEvent.ACTION_DOWN:
  100. break;
  101. default:
  102. break;
  103. }
  104. returntrue;//取消ListView滑動.
  105. }
  106. returnsuper.onTouchEvent(ev);
  107. }
  108. /**
  109. *準備拖動,初始化拖動項的圖像
  110. *
  111. *@parambm
  112. *@paramy
  113. */
  114. privatevoidstartDrag(Bitmapbm,inty){
  115. //stopDrag();
  116. /***
  117. *初始化window.
  118. */
  119. windowParams=newWindowManager.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. ImageViewimageView=newImageView(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. *@paramy
  141. */
  142. publicvoidonDrag(inty){
  143. intdrag_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. inttempPosition=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. publicvoiddoScroller(inty){
  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. elseif(y>downScrollBounce){
  169. current_Step=-(step+(y-downScrollBounce))/10;//時時步伐
  170. }else{
  171. current_Step=0;
  172. }
  173. //獲取你拖拽滑動到位置及顯示item相應的view上(註:可顯示部分)(position)
  174. Viewview=getChildAt(dragPosition-getFirstVisiblePosition());
  175. //真正滾動的方法setSelectionFromTop()
  176. setSelectionFromTop(dragPosition,view.getTop()+current_Step);
  177. }
  178. /**
  179. *停止拖動,刪除影像
  180. */
  181. publicvoidstopDrag(){
  182. if(dragImageView!=null){
  183. windowManager.removeView(dragImageView);
  184. dragImageView=null;
  185. }
  186. }
  187. /**
  188. *拖動放下的時候
  189. *
  190. *@paramy
  191. */
  192. publicvoidonDrop(inty){
  193. //為了避免滑動到分割線的時候,返回-1的問題
  194. inttempPosition=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. }elseif(y>getChildAt(getChildCount()-1).getBottom()){
  204. //超出下邊界
  205. dragPosition=getAdapter().getCount()-1;
  206. }
  207. //數據交換
  208. if(dragPosition<getAdapter().getCount()){
  209. DragListAdapteradapter=(DragListAdapter)getAdapter();
  210. adapter.update(dragSrcPosition,dragPosition);
  211. }
  212. }
  213. }

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 plaincopyprint?

  1. /***
  2. *自定義適配器
  3. *
  4. *@authorzhangjia
  5. *
  6. */
  7. classDragListAdapterextendsBaseAdapter{
  8. privateArrayList<String>arrayTitles;
  9. privateArrayList<Integer>arrayDrawables;
  10. privateContextcontext;
  11. publicDragListAdapter(Contextcontext,ArrayList<String>arrayTitles,
  12. ArrayList<Integer>arrayDrawables){
  13. this.context=context;
  14. this.arrayTitles=arrayTitles;
  15. this.arrayDrawables=arrayDrawables;
  16. }
  17. @Override
  18. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  19. Viewview=convertView;
  20. /***
  21. *在這裡儘可能每次都進行實例化新的,這樣在拖拽ListView的時候不會出現錯亂.
  22. *具體原因不明,不過這樣經過測試,目前沒有發現錯亂。雖說效率不高,但是做拖拽LisView足夠了。
  23. */
  24. view=LayoutInflater.from(context).inflate(
  25. R.layout.drag_list_item,null);
  26. TextViewtextView=(TextView)view
  27. .findViewById(R.id.tv_drag_list_item_text);
  28. ImageViewimageView=(ImageView)view
  29. .findViewById(R.id.iv_drag_list_item_1);
  30. imageView.setImageResource(arrayDrawables.get(position));
  31. textView.setText(arrayTitles.get(position));
  32. returnview;
  33. }
  34. /***
  35. *動態修改ListVIiw的方位.
  36. *
  37. *@paramstart
  38. *點擊移動的position
  39. *@paramdown
  40. *鬆開時候的position
  41. */
  42. publicvoidupdate(intstart,intdown){
  43. //獲取刪除的東東.
  44. Stringtitle=arrayTitles.get(start);
  45. intdrawable_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. publicintgetCount(){
  54. returnTitle.length;
  55. }
  56. @Override
  57. publicObjectgetItem(intposition){
  58. returnTitle[position];
  59. }
  60. @Override
  61. publiclonggetItemId(intposition){
  62. returnposition;
  63. }
  64. }

/*** * 自定義適配器 * * @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 plaincopyprint?
  1. packagecom.jj.drag;
  2. importandroid.content.Context;
  3. importandroid.graphics.Bitmap;
  4. importandroid.graphics.Color;
  5. importandroid.os.AsyncTask;
  6. importandroid.util.AttributeSet;
  7. importandroid.util.Log;
  8. importandroid.view.Gravity;
  9. importandroid.view.MotionEvent;
  10. importandroid.view.View;
  11. importandroid.view.ViewConfiguration;
  12. importandroid.view.ViewGroup;
  13. importandroid.view.WindowManager;
  14. importandroid.widget.AbsListView;
  15. importandroid.widget.AbsListView.OnScrollListener;
  16. importandroid.widget.AdapterView;
  17. importandroid.widget.ImageView;
  18. importandroid.widget.ListView;
  19. importcom.jj.drag.MainActivity.DragListAdapter;
  20. publicclassDragListViewextendsListView{
  21. privateWindowManagerwindowManager;//windows窗口控制類
  22. privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
  23. privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
  24. privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
  25. privateintdragSrcPosition;//手指拖動項原始在列表中的位置
  26. privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
  27. privateintdragPoint;//在當前數據項中的位置
  28. privateintdragOffset;//當前視圖和屏幕的距離(這裡只使用了y方向上)
  29. privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
  30. privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
  31. privatefinalstaticintstep=1;//ListView滑動步伐.
  32. privateintcurrent_Step;//當前步伐.
  33. privateinttemChangId;//臨時交換id
  34. privatebooleanisLock;//是否上鎖.
  35. publicvoidsetLock(booleanisLock){
  36. this.isLock=isLock;
  37. }
  38. publicDragListView(Contextcontext,AttributeSetattrs){
  39. super(context,attrs);
  40. scaledTouchSlop=ViewConfiguration.get(context).getScaledTouchSlop();
  41. }
  42. /***
  43. *touch事件攔截在這裡我進行相應攔截,
  44. */
  45. @Override
  46. publicbooleanonInterceptTouchEvent(MotionEventev){
  47. //按下
  48. if(ev.getAction()==MotionEvent.ACTION_DOWN&&!isLock){
  49. intx=(int)ev.getX();//獲取相對與ListView的x坐標
  50. inty=(int)ev.getY();//獲取相應與ListView的y坐標
  51. temChangId=dragSrcPosition=dragPosition=pointToPosition(x,y);
  52. //無效不進行處理
  53. if(dragPosition==AdapterView.INVALID_POSITION){
  54. returnsuper.onInterceptTouchEvent(ev);
  55. }
  56. //獲取當前位置的視圖(可見狀態)
  57. ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
  58. -getFirstVisiblePosition());
  59. //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
  60. dragPoint=y-itemView.getTop();
  61. //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
  62. dragOffset=(int)(ev.getRawY()-y);
  63. //獲取可拖拽的圖標
  64. Viewdragger=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. Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
  72. startDrag(bm,y);//初始化影像
  73. }
  74. returnfalse;
  75. }
  76. returnsuper.onInterceptTouchEvent(ev);
  77. }
  78. /**
  79. *觸摸事件處理
  80. */
  81. @Override
  82. publicbooleanonTouchEvent(MotionEventev){
  83. //item的view不為空,且獲取的dragPosition有效
  84. if(dragImageView!=null&&dragPosition!=INVALID_POSITION
  85. &&!isLock){
  86. intaction=ev.getAction();
  87. switch(action){
  88. caseMotionEvent.ACTION_UP:
  89. intupY=(int)ev.getY();
  90. stopDrag();
  91. onDrop(upY);
  92. break;
  93. caseMotionEvent.ACTION_MOVE:
  94. intmoveY=(int)ev.getY();
  95. onDrag(moveY);
  96. break;
  97. caseMotionEvent.ACTION_DOWN:
  98. break;
  99. default:
  100. break;
  101. }
  102. returntrue;//取消ListView滑動.
  103. }
  104. returnsuper.onTouchEvent(ev);
  105. }
  106. /**
  107. *準備拖動,初始化拖動項的圖像
  108. *
  109. *@parambm
  110. *@paramy
  111. */
  112. privatevoidstartDrag(Bitmapbm,inty){
  113. //stopDrag();
  114. /***
  115. *初始化window.
  116. */
  117. windowParams=newWindowManager.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. ImageViewimageView=newImageView(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. *@paramy
  139. */
  140. publicvoidonDrag(inty){
  141. intdrag_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. inttempPosition=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. publicvoiddoScroller(inty){
  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. elseif(y>downScrollBounce){
  168. current_Step=-(step+(y-downScrollBounce))/10;//時時步伐
  169. }else{
  170. current_Step=0;
  171. }
  172. //獲取你拖拽滑動到位置及顯示item相應的view上(註:可顯示部分)(position)
  173. Viewview=getChildAt(dragPosition-getFirstVisiblePosition());
  174. //真正滾動的方法setSelectionFromTop()
  175. setSelectionFromTop(dragPosition,view.getTop()+current_Step);
  176. }
  177. /**
  178. *停止拖動,刪除影像
  179. */
  180. publicvoidstopDrag(){
  181. if(dragImageView!=null){
  182. windowManager.removeView(dragImageView);
  183. dragImageView=null;
  184. }
  185. }
  186. /***
  187. *拖動時時change
  188. */
  189. privatevoidonChange(inty){
  190. //數據交換
  191. if(dragPosition<getAdapter().getCount()){
  192. DragListAdapteradapter=(DragListAdapter)getAdapter();
  193. adapter.isHidden=false;
  194. if(dragPosition!=temChangId){
  195. adapter.update(temChangId,dragPosition);
  196. temChangId=dragPosition;//將點擊最初所在位置position付給臨時的,用於判斷是否換位.
  197. }
  198. }
  199. //為了避免滑動到分割線的時候,返回-1的問題
  200. inttempPosition=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. }elseif(y>getChildAt(getChildCount()-1).getBottom()){
  210. //超出下邊界
  211. dragPosition=getAdapter().getCount()-1;
  212. }
  213. }
  214. /**
  215. *拖動放下的時候
  216. *
  217. *@paramy
  218. */
  219. publicvoidonDrop(inty){
  220. //數據交換
  221. if(dragPosition<getAdapter().getCount()){
  222. DragListAdapteradapter=(DragListAdapter)getAdapter();
  223. adapter.isHidden=false;
  224. adapter.notifyDataSetChanged();//刷新.
  225. }
  226. }
  227. }

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 plaincopyprint?
  1. packagecom.jj.draggrid;
  2. importjava.util.logging.Handler;
  3. importcom.jj.draggrid.MainActivity.DragGridAdapter;
  4. importandroid.content.Context;
  5. importandroid.graphics.Bitmap;
  6. importandroid.graphics.PixelFormat;
  7. importandroid.util.AttributeSet;
  8. importandroid.util.Log;
  9. importandroid.view.Gravity;
  10. importandroid.view.MotionEvent;
  11. importandroid.view.View;
  12. importandroid.view.ViewGroup;
  13. importandroid.view.WindowManager;
  14. importandroid.widget.AdapterView;
  15. importandroid.widget.BaseAdapter;
  16. importandroid.widget.GridView;
  17. importandroid.widget.ImageView;
  18. importandroid.widget.Toast;
  19. /***
  20. *自定義拖拽GridView
  21. *
  22. *@authorzhangjia
  23. *
  24. */
  25. publicclassDragGridViewextendsGridView{
  26. privateWindowManagerwindowManager;//windows窗口控制類
  27. privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
  28. privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
  29. privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
  30. privateintdragSrcPosition;//手指拖動項原始在列表中的位置
  31. privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
  32. privateintdragPointX;//在當前數據項中的位置
  33. privateintdragPointY;//在當前數據項中的位置
  34. privateintdragOffsetX;//當前視圖和屏幕的距離(這裡只使用了x方向上)
  35. privateintdragOffsetY;//當前視圖和屏幕的距離(這裡只使用了y方向上)
  36. privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
  37. privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
  38. privateinttemChangId;//臨時交換id
  39. privatebooleanisDoTouch=false;//touch是否可用
  40. privatebooleanisHide=false;//是否隱藏
  41. privateHandlerhandler;
  42. publicvoidsetDoTouch(booleanisDoTouch){
  43. this.isDoTouch=isDoTouch;
  44. }
  45. publicDragGridView(Contextcontext,AttributeSetattrs){
  46. super(context,attrs);
  47. }
  48. @Override
  49. publicbooleanonInterceptTouchEvent(MotionEventev){
  50. if(ev.getAction()==MotionEvent.ACTION_DOWN){
  51. intx=(int)ev.getX();
  52. inty=(int)ev.getY();
  53. temChangId=dragSrcPosition=dragPosition=pointToPosition(x,y);
  54. if(dragPosition==AdapterView.INVALID_POSITION){
  55. returnsuper.onInterceptTouchEvent(ev);
  56. }
  57. ViewGroupitemView=(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. Viewdragger=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. Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());
  75. startDrag(bm,x,y);//初始話映像
  76. dragger.setVisibility(View.INVISIBLE);//隱藏該項.
  77. }
  78. }
  79. returnsuper.onInterceptTouchEvent(ev);
  80. }
  81. @Override
  82. publicbooleanonTouchEvent(MotionEventev){
  83. if(dragImageView!=null&&dragPosition!=INVALID_POSITION
  84. &&isDoTouch){
  85. intaction=ev.getAction();
  86. switch(action){
  87. /***
  88. *
  89. */
  90. caseMotionEvent.ACTION_UP:
  91. intupX=(int)ev.getX();
  92. intupY=(int)ev.getY();
  93. stopDrag();//刪除映像
  94. onDrop(upX,upY);//鬆開
  95. //isDoTouch=false;
  96. break;
  97. /***
  98. *拖拽item
  99. *
  100. */
  101. caseMotionEvent.ACTION_MOVE:
  102. intmoveX=(int)ev.getX();
  103. intmoveY=(int)ev.getY();
  104. onDrag(moveX,moveY);//拖拽
  105. break;
  106. caseMotionEvent.ACTION_DOWN:
  107. intdownX=(int)ev.getX();
  108. intdownY=(int)ev.getY();
  109. onHide(downX,downY);//隱藏該項
  110. break;
  111. default:
  112. break;
  113. }
  114. returntrue;
  115. }
  116. returnsuper.onTouchEvent(ev);
  117. }
  118. /**
  119. *準備拖動,初始化拖動項的圖像
  120. *
  121. *@parambm
  122. *@paramy
  123. */
  124. publicvoidstartDrag(Bitmapbm,intx,inty){
  125. windowParams=newWindowManager.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. ImageViewimageView=newImageView(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. privatevoidonChange(intx,inty){
  146. //獲取適配器
  147. DragGridAdapteradapter=(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. inttempPosition=pointToPosition(x,y);
  158. if(tempPosition!=INVALID_POSITION){
  159. dragPosition=tempPosition;
  160. }
  161. }
  162. /***
  163. *拖動執行,在Move方法中執行
  164. *
  165. *@paramx
  166. *@paramy
  167. */
  168. publicvoidonDrag(intx,inty){
  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. privatevoidonHide(intx,inty){
  187. //獲取適配器
  188. DragGridAdapteradapter=(DragGridAdapter)getAdapter();
  189. //為了避免滑動到分割線的時候,返回-1的問題
  190. inttempPosition=pointToPosition(x,y);
  191. if(tempPosition!=INVALID_POSITION){
  192. dragPosition=tempPosition;
  193. }
  194. adapter.setIsHidePosition(dragPosition);
  195. }
  196. /**
  197. *停止拖動,刪除影像
  198. */
  199. publicvoidstopDrag(){
  200. if(dragImageView!=null){
  201. windowManager.removeView(dragImageView);
  202. dragImageView=null;
  203. }
  204. }
  205. /***
  206. *拖動放下的時候
  207. *
  208. *@paramx
  209. *@paramy
  210. */
  211. publicvoidonDrop(intx,inty){
  212. DragGridAdapteradapter=(DragGridAdapter)getAdapter();
  213. adapter.setIsHidePosition(-1);//不進行隱藏
  214. }
  215. }

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 plaincopyprint?
  1. gv_main.setOnItemClickListener(newOnItemClickListener(){
  2. @Override
  3. publicvoidonItemClick(AdapterView<?>parent,Viewview,
  4. intposition,longid){
  5. gv_main.setDoTouch(false);
  6. Toast.makeText(MainActivity.this,
  7. adapter.getItemId(position)+"",1).show();
  8. }
  9. });
  10. gv_main.setOnItemLongClickListener(newOnItemLongClickListener(){
  11. @Override
  12. publicbooleanonItemLongClick(AdapterView<?>parent,Viewview,
  13. intposition,longid){
  14. gv_main.setDoTouch(true);
  15. returntrue;
  16. }
  17. });

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.網上有寫應該就是這麼搞的,具體實現不知道,不過這種方案可以實現,有時間接著續.


推薦閱讀:
查看原文 >>
相关文章