android ListView和GridView拖拽移位具體實現及拓展
關於ListView拖拽移動位置,想必大家並不陌生,比較不錯的軟體都用到如此功能了.如:搜狐,網易,百度等,但是相比來說還是百度的用戶體驗較好,不偏心了,下面看幾個示例:
首先說一下:拖拽ListView的item就不應該可以任意移動,只應該在ListView所在的範圍內,而網易的你看看我都可以移動到狀態欄了,雖然你做了處理,但是用戶體驗我個人感覺不好,在看看百度的,不僅控制了移動範圍,更不錯的百度的移動起來會時時的換位,看起來相當的形象,所以我認為這樣相當的棒.
說明一點,我沒有那麼有才,我也是看別人代碼,然後自己整理下.在這裡就簡單記載一下.
首先對touch事件的處理,從應用中,我們可以得出,在我們點擊後面拖拉圖標後,就會創建一個item的影像視圖.並且可以移動該影像,而此時的ListView不應該有touch事件.
onInterceptTouchEvent方法.
[java] view plaincopyprint?- /***
- *touch事件攔截
- */
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- //按下
- if(ev.getAction()==MotionEvent.ACTION_DOWN){
- intx=(int)ev.getX();//獲取相對與ListView的x坐標
- inty=(int)ev.getY();//獲取相應與ListView的y坐標
- dragSrcPosition=dragPosition=pointToPosition(x,y);
- //無效不進行處理
- if(dragPosition==AdapterView.INVALID_POSITION){
- returnsuper.onInterceptTouchEvent(ev);
- }
- //獲取當前位置的視圖(可見狀態)
- ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
- -getFirstVisiblePosition());
- //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
- dragPoint=y-itemView.getTop();
- //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
- dragOffset=(int)(ev.getRawY()-y);
- //獲取可拖拽的圖標
- Viewdragger=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.
- Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
- startDrag(bm,y);//初始化影像
- }
- //returnfalse;
- }
- returnsuper.onInterceptTouchEvent(ev);
- }
/*** * 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?- /**
- *觸摸事件處理
- */
- @Override
- publicbooleanonTouchEvent(MotionEventev){
- //item的view不為空,且獲取的dragPosition有效
- if(dragImageView!=null&&dragPosition!=INVALID_POSITION){
- intaction=ev.getAction();
- switch(action){
- caseMotionEvent.ACTION_UP:
- intupY=(int)ev.getY();
- stopDrag();
- onDrop(upY);
- break;
- caseMotionEvent.ACTION_MOVE:
- intmoveY=(int)ev.getY();
- onDrag(moveY);
- break;
- caseMotionEvent.ACTION_DOWN:
- break;
- default:
- break;
- }
- returntrue;//取消ListView滑動.
- }
- returnsuper.onTouchEvent(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); }
簡單說明:首先在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?- packagecom.jj.drag;
- importandroid.content.Context;
- importandroid.graphics.Bitmap;
- importandroid.os.AsyncTask;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.Gravity;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.ViewConfiguration;
- importandroid.view.ViewGroup;
- importandroid.view.WindowManager;
- importandroid.widget.AbsListView;
- importandroid.widget.AbsListView.OnScrollListener;
- importandroid.widget.AdapterView;
- importandroid.widget.ImageView;
- importandroid.widget.ListView;
- importcom.jj.drag.MainActivity.DragListAdapter;
- /***
- *自定義拖拽ListView
- *
- *@authorzhangjia
- *
- */
- publicclassDragListViewextendsListView{
- privateWindowManagerwindowManager;//windows窗口控制類
- privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
- privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
- privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
- privateintdragSrcPosition;//手指拖動項原始在列表中的位置
- privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
- privateintdragPoint;//在當前數據項中的位置
- privateintdragOffset;//當前視圖和屏幕的距離(這裡只使用了y方向上)
- privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
- privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
- privatefinalstaticintstep=1;//ListView滑動步伐.
- privateintcurrent_Step;//當前步伐.
- /***
- *構造方法
- *
- *@paramcontext
- *@paramattrs
- */
- publicDragListView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- }
- /***
- *touch事件攔截
- */
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- //按下
- if(ev.getAction()==MotionEvent.ACTION_DOWN){
- intx=(int)ev.getX();//獲取相對與ListView的x坐標
- inty=(int)ev.getY();//獲取相應與ListView的y坐標
- dragSrcPosition=dragPosition=pointToPosition(x,y);
- //無效不進行處理
- if(dragPosition==AdapterView.INVALID_POSITION){
- returnsuper.onInterceptTouchEvent(ev);
- }
- //獲取當前位置的視圖(可見狀態)
- ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
- -getFirstVisiblePosition());
- //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
- dragPoint=y-itemView.getTop();
- //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
- dragOffset=(int)(ev.getRawY()-y);
- //獲取可拖拽的圖標
- Viewdragger=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.
- Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
- startDrag(bm,y);//初始化影像
- }
- }
- returnsuper.onInterceptTouchEvent(ev);
- }
- /**
- *觸摸事件處理
- */
- @Override
- publicbooleanonTouchEvent(MotionEventev){
- //item的view不為空,且獲取的dragPosition有效
- if(dragImageView!=null&&dragPosition!=INVALID_POSITION){
- intaction=ev.getAction();
- switch(action){
- caseMotionEvent.ACTION_UP:
- intupY=(int)ev.getY();
- stopDrag();
- onDrop(upY);
- break;
- caseMotionEvent.ACTION_MOVE:
- intmoveY=(int)ev.getY();
- onDrag(moveY);
- break;
- caseMotionEvent.ACTION_DOWN:
- break;
- default:
- break;
- }
- returntrue;//取消ListView滑動.
- }
- returnsuper.onTouchEvent(ev);
- }
- /**
- *準備拖動,初始化拖動項的圖像
- *
- *@parambm
- *@paramy
- */
- privatevoidstartDrag(Bitmapbm,inty){
- //stopDrag();
- /***
- *初始化window.
- */
- windowParams=newWindowManager.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;//窗口所使用的動畫設置
- ImageViewimageView=newImageView(getContext());
- imageView.setImageBitmap(bm);
- windowManager=(WindowManager)getContext().getSystemService("window");
- windowManager.addView(imageView,windowParams);
- dragImageView=imageView;
- }
- /**
- *拖動執行,在Move方法中執行
- *
- *@paramy
- */
- publicvoidonDrag(inty){
- intdrag_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的問題
- inttempPosition=pointToPosition(0,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- doScroller(y);
- }
- /***
- *ListView的移動.
- *要明白移動原理:當映像移動到下端的時候,ListView向上滑動,當映像移動到上端的時候,ListView要向下滑動。正好和實際的相反.
- *
- */
- publicvoiddoScroller(inty){
- Log.e("jj","y="+y);
- Log.e("jj","upScrollBounce="+upScrollBounce);
- //ListView需要下滑
- if(y<upScrollBounce){
- current_Step=step+(upScrollBounce-y)/10;//時時步伐
- }//ListView需要上滑
- elseif(y>downScrollBounce){
- current_Step=-(step+(y-downScrollBounce))/10;//時時步伐
- }else{
- current_Step=0;
- }
- //獲取你拖拽滑動到位置及顯示item相應的view上(註:可顯示部分)(position)
- Viewview=getChildAt(dragPosition-getFirstVisiblePosition());
- //真正滾動的方法setSelectionFromTop()
- setSelectionFromTop(dragPosition,view.getTop()+current_Step);
- }
- /**
- *停止拖動,刪除影像
- */
- publicvoidstopDrag(){
- if(dragImageView!=null){
- windowManager.removeView(dragImageView);
- dragImageView=null;
- }
- }
- /**
- *拖動放下的時候
- *
- *@paramy
- */
- publicvoidonDrop(inty){
- //為了避免滑動到分割線的時候,返回-1的問題
- inttempPosition=pointToPosition(0,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- //超出邊界處理(如果向上超過第二項Top的話,那麼就放置在第一個位置)
- if(y<getChildAt(0).getTop()){
- //超出上邊界
- dragPosition=0;
- //如果拖動超過最後一項的最下邊那麼就防止在最下邊
- }elseif(y>getChildAt(getChildCount()-1).getBottom()){
- //超出下邊界
- dragPosition=getAdapter().getCount()-1;
- }
- //數據交換
- if(dragPosition<getAdapter().getCount()){
- DragListAdapteradapter=(DragListAdapter)getAdapter();
- adapter.update(dragSrcPosition,dragPosition);
- }
- }
- }
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?
- /***
- *自定義適配器
- *
- *@authorzhangjia
- *
- */
- classDragListAdapterextendsBaseAdapter{
- privateArrayList<String>arrayTitles;
- privateArrayList<Integer>arrayDrawables;
- privateContextcontext;
- publicDragListAdapter(Contextcontext,ArrayList<String>arrayTitles,
- ArrayList<Integer>arrayDrawables){
- this.context=context;
- this.arrayTitles=arrayTitles;
- this.arrayDrawables=arrayDrawables;
- }
- @Override
- publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
- Viewview=convertView;
- /***
- *在這裡儘可能每次都進行實例化新的,這樣在拖拽ListView的時候不會出現錯亂.
- *具體原因不明,不過這樣經過測試,目前沒有發現錯亂。雖說效率不高,但是做拖拽LisView足夠了。
- */
- view=LayoutInflater.from(context).inflate(
- R.layout.drag_list_item,null);
- TextViewtextView=(TextView)view
- .findViewById(R.id.tv_drag_list_item_text);
- ImageViewimageView=(ImageView)view
- .findViewById(R.id.iv_drag_list_item_1);
- imageView.setImageResource(arrayDrawables.get(position));
- textView.setText(arrayTitles.get(position));
- returnview;
- }
- /***
- *動態修改ListVIiw的方位.
- *
- *@paramstart
- *點擊移動的position
- *@paramdown
- *鬆開時候的position
- */
- publicvoidupdate(intstart,intdown){
- //獲取刪除的東東.
- Stringtitle=arrayTitles.get(start);
- intdrawable_id=arrayDrawables.get(start);
- arrayTitles.remove(start);//刪除該項
- arrayDrawables.remove(start);//刪除該項
- arrayTitles.add(down,title);//添加刪除項
- arrayDrawables.add(down,drawable_id);//添加刪除項
- notifyDataSetChanged();//刷新ListView
- }
- @Override
- publicintgetCount(){
- returnTitle.length;
- }
- @Override
- publicObjectgetItem(intposition){
- returnTitle[position];
- }
- @Override
- publiclonggetItemId(intposition){
- returnposition;
- }
- }
/*** * 自定義適配器 * * @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?- packagecom.jj.drag;
- importandroid.content.Context;
- importandroid.graphics.Bitmap;
- importandroid.graphics.Color;
- importandroid.os.AsyncTask;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.Gravity;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.ViewConfiguration;
- importandroid.view.ViewGroup;
- importandroid.view.WindowManager;
- importandroid.widget.AbsListView;
- importandroid.widget.AbsListView.OnScrollListener;
- importandroid.widget.AdapterView;
- importandroid.widget.ImageView;
- importandroid.widget.ListView;
- importcom.jj.drag.MainActivity.DragListAdapter;
- publicclassDragListViewextendsListView{
- privateWindowManagerwindowManager;//windows窗口控制類
- privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
- privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
- privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
- privateintdragSrcPosition;//手指拖動項原始在列表中的位置
- privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
- privateintdragPoint;//在當前數據項中的位置
- privateintdragOffset;//當前視圖和屏幕的距離(這裡只使用了y方向上)
- privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
- privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
- privatefinalstaticintstep=1;//ListView滑動步伐.
- privateintcurrent_Step;//當前步伐.
- privateinttemChangId;//臨時交換id
- privatebooleanisLock;//是否上鎖.
- publicvoidsetLock(booleanisLock){
- this.isLock=isLock;
- }
- publicDragListView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- scaledTouchSlop=ViewConfiguration.get(context).getScaledTouchSlop();
- }
- /***
- *touch事件攔截在這裡我進行相應攔截,
- */
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- //按下
- if(ev.getAction()==MotionEvent.ACTION_DOWN&&!isLock){
- intx=(int)ev.getX();//獲取相對與ListView的x坐標
- inty=(int)ev.getY();//獲取相應與ListView的y坐標
- temChangId=dragSrcPosition=dragPosition=pointToPosition(x,y);
- //無效不進行處理
- if(dragPosition==AdapterView.INVALID_POSITION){
- returnsuper.onInterceptTouchEvent(ev);
- }
- //獲取當前位置的視圖(可見狀態)
- ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
- -getFirstVisiblePosition());
- //獲取到的dragPoint其實就是在你點擊指定item項中的高度.
- dragPoint=y-itemView.getTop();
- //這個值是固定的:其實就是ListView這個控制項與屏幕最頂部的距離(一般為標題欄+狀態欄).
- dragOffset=(int)(ev.getRawY()-y);
- //獲取可拖拽的圖標
- Viewdragger=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.
- Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());//根據cache創建一個新的bitmap對象.
- startDrag(bm,y);//初始化影像
- }
- returnfalse;
- }
- returnsuper.onInterceptTouchEvent(ev);
- }
- /**
- *觸摸事件處理
- */
- @Override
- publicbooleanonTouchEvent(MotionEventev){
- //item的view不為空,且獲取的dragPosition有效
- if(dragImageView!=null&&dragPosition!=INVALID_POSITION
- &&!isLock){
- intaction=ev.getAction();
- switch(action){
- caseMotionEvent.ACTION_UP:
- intupY=(int)ev.getY();
- stopDrag();
- onDrop(upY);
- break;
- caseMotionEvent.ACTION_MOVE:
- intmoveY=(int)ev.getY();
- onDrag(moveY);
- break;
- caseMotionEvent.ACTION_DOWN:
- break;
- default:
- break;
- }
- returntrue;//取消ListView滑動.
- }
- returnsuper.onTouchEvent(ev);
- }
- /**
- *準備拖動,初始化拖動項的圖像
- *
- *@parambm
- *@paramy
- */
- privatevoidstartDrag(Bitmapbm,inty){
- //stopDrag();
- /***
- *初始化window.
- */
- windowParams=newWindowManager.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;//窗口所使用的動畫設置
- ImageViewimageView=newImageView(getContext());
- imageView.setImageBitmap(bm);
- windowManager=(WindowManager)getContext().getSystemService("window");
- windowManager.addView(imageView,windowParams);
- dragImageView=imageView;
- }
- /**
- *拖動執行,在Move方法中執行
- *
- *@paramy
- */
- publicvoidonDrag(inty){
- intdrag_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的問題
- inttempPosition=pointToPosition(0,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- onChange(y);//時時交換
- doScroller(y);//listview移動.
- }
- /***
- *ListView的移動.
- *要明白移動原理:當我移動到下端的時候,ListView向上滑動,當我移動到上端的時候,ListView要向下滑動。正好和實際的相反.
- *
- */
- publicvoiddoScroller(inty){
- //Log.e("jj","y="+y);
- //Log.e("jj","upScrollBounce="+upScrollBounce);
- //ListView需要下滑
- if(y<upScrollBounce){
- current_Step=step+(upScrollBounce-y)/10;//時時步伐
- }//ListView需要上滑
- elseif(y>downScrollBounce){
- current_Step=-(step+(y-downScrollBounce))/10;//時時步伐
- }else{
- current_Step=0;
- }
- //獲取你拖拽滑動到位置及顯示item相應的view上(註:可顯示部分)(position)
- Viewview=getChildAt(dragPosition-getFirstVisiblePosition());
- //真正滾動的方法setSelectionFromTop()
- setSelectionFromTop(dragPosition,view.getTop()+current_Step);
- }
- /**
- *停止拖動,刪除影像
- */
- publicvoidstopDrag(){
- if(dragImageView!=null){
- windowManager.removeView(dragImageView);
- dragImageView=null;
- }
- }
- /***
- *拖動時時change
- */
- privatevoidonChange(inty){
- //數據交換
- if(dragPosition<getAdapter().getCount()){
- DragListAdapteradapter=(DragListAdapter)getAdapter();
- adapter.isHidden=false;
- if(dragPosition!=temChangId){
- adapter.update(temChangId,dragPosition);
- temChangId=dragPosition;//將點擊最初所在位置position付給臨時的,用於判斷是否換位.
- }
- }
- //為了避免滑動到分割線的時候,返回-1的問題
- inttempPosition=pointToPosition(0,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- //超出邊界處理(如果向上超過第二項Top的話,那麼就放置在第一個位置)
- if(y<getChildAt(0).getTop()){
- //超出上邊界
- dragPosition=0;
- //如果拖動超過最後一項的最下邊那麼就防止在最下邊
- }elseif(y>getChildAt(getChildCount()-1).getBottom()){
- //超出下邊界
- dragPosition=getAdapter().getCount()-1;
- }
- }
- /**
- *拖動放下的時候
- *
- *@paramy
- */
- publicvoidonDrop(inty){
- //數據交換
- if(dragPosition<getAdapter().getCount()){
- DragListAdapteradapter=(DragListAdapter)getAdapter();
- adapter.isHidden=false;
- adapter.notifyDataSetChanged();//刷新.
- }
- }
- }
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?- packagecom.jj.draggrid;
- importjava.util.logging.Handler;
- importcom.jj.draggrid.MainActivity.DragGridAdapter;
- importandroid.content.Context;
- importandroid.graphics.Bitmap;
- importandroid.graphics.PixelFormat;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.Gravity;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.view.WindowManager;
- importandroid.widget.AdapterView;
- importandroid.widget.BaseAdapter;
- importandroid.widget.GridView;
- importandroid.widget.ImageView;
- importandroid.widget.Toast;
- /***
- *自定義拖拽GridView
- *
- *@authorzhangjia
- *
- */
- publicclassDragGridViewextendsGridView{
- privateWindowManagerwindowManager;//windows窗口控制類
- privateWindowManager.LayoutParamswindowParams;//用於控制拖拽項的顯示的參數
- privateintscaledTouchSlop;//判斷滑動的一個距離,scroll的時候會用到(24)
- privateImageViewdragImageView;//被拖拽的項(item),其實就是一個ImageView
- privateintdragSrcPosition;//手指拖動項原始在列表中的位置
- privateintdragPosition;//手指點擊準備拖動的時候,當前拖動項在列表中的位置.
- privateintdragPointX;//在當前數據項中的位置
- privateintdragPointY;//在當前數據項中的位置
- privateintdragOffsetX;//當前視圖和屏幕的距離(這裡只使用了x方向上)
- privateintdragOffsetY;//當前視圖和屏幕的距離(這裡只使用了y方向上)
- privateintupScrollBounce;//拖動的時候,開始向上滾動的邊界
- privateintdownScrollBounce;//拖動的時候,開始向下滾動的邊界
- privateinttemChangId;//臨時交換id
- privatebooleanisDoTouch=false;//touch是否可用
- privatebooleanisHide=false;//是否隱藏
- privateHandlerhandler;
- publicvoidsetDoTouch(booleanisDoTouch){
- this.isDoTouch=isDoTouch;
- }
- publicDragGridView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- }
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- if(ev.getAction()==MotionEvent.ACTION_DOWN){
- intx=(int)ev.getX();
- inty=(int)ev.getY();
- temChangId=dragSrcPosition=dragPosition=pointToPosition(x,y);
- if(dragPosition==AdapterView.INVALID_POSITION){
- returnsuper.onInterceptTouchEvent(ev);
- }
- ViewGroupitemView=(ViewGroup)getChildAt(dragPosition
- -getFirstVisiblePosition());
- dragPointX=x-itemView.getLeft();
- dragPointY=y-itemView.getTop();
- dragOffsetX=(int)(ev.getRawX()-x);
- dragOffsetY=(int)(ev.getRawY()-y);
- Viewdragger=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);
- Bitmapbm=Bitmap.createBitmap(itemView.getDrawingCache());
- startDrag(bm,x,y);//初始話映像
- dragger.setVisibility(View.INVISIBLE);//隱藏該項.
- }
- }
- returnsuper.onInterceptTouchEvent(ev);
- }
- @Override
- publicbooleanonTouchEvent(MotionEventev){
- if(dragImageView!=null&&dragPosition!=INVALID_POSITION
- &&isDoTouch){
- intaction=ev.getAction();
- switch(action){
- /***
- *
- */
- caseMotionEvent.ACTION_UP:
- intupX=(int)ev.getX();
- intupY=(int)ev.getY();
- stopDrag();//刪除映像
- onDrop(upX,upY);//鬆開
- //isDoTouch=false;
- break;
- /***
- *拖拽item
- *
- */
- caseMotionEvent.ACTION_MOVE:
- intmoveX=(int)ev.getX();
- intmoveY=(int)ev.getY();
- onDrag(moveX,moveY);//拖拽
- break;
- caseMotionEvent.ACTION_DOWN:
- intdownX=(int)ev.getX();
- intdownY=(int)ev.getY();
- onHide(downX,downY);//隱藏該項
- break;
- default:
- break;
- }
- returntrue;
- }
- returnsuper.onTouchEvent(ev);
- }
- /**
- *準備拖動,初始化拖動項的圖像
- *
- *@parambm
- *@paramy
- */
- publicvoidstartDrag(Bitmapbm,intx,inty){
- windowParams=newWindowManager.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;
- ImageViewimageView=newImageView(getContext());
- imageView.setImageBitmap(bm);
- windowManager=(WindowManager)getContext().getSystemService("window");
- windowManager.addView(imageView,windowParams);
- dragImageView=imageView;
- }
- /***
- *拖動時時change
- */
- privatevoidonChange(intx,inty){
- //獲取適配器
- DragGridAdapteradapter=(DragGridAdapter)getAdapter();
- //數據交換
- if(dragPosition<getAdapter().getCount()){
- //不相等的情況下要進行換位,相等的情況下說明正在移動
- if(dragPosition!=temChangId){
- adapter.update(temChangId,dragPosition);//進行換位
- temChangId=dragPosition;//將點擊最初所在位置position付給臨時的,用於判斷是否換位.
- }
- }
- //為了避免滑動到分割線的時候,返回-1的問題
- inttempPosition=pointToPosition(x,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- }
- /***
- *拖動執行,在Move方法中執行
- *
- *@paramx
- *@paramy
- */
- publicvoidonDrag(intx,inty){
- //移動
- 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);
- }
- }
- /***
- *隱藏該選項
- */
- privatevoidonHide(intx,inty){
- //獲取適配器
- DragGridAdapteradapter=(DragGridAdapter)getAdapter();
- //為了避免滑動到分割線的時候,返回-1的問題
- inttempPosition=pointToPosition(x,y);
- if(tempPosition!=INVALID_POSITION){
- dragPosition=tempPosition;
- }
- adapter.setIsHidePosition(dragPosition);
- }
- /**
- *停止拖動,刪除影像
- */
- publicvoidstopDrag(){
- if(dragImageView!=null){
- windowManager.removeView(dragImageView);
- dragImageView=null;
- }
- }
- /***
- *拖動放下的時候
- *
- *@paramx
- *@paramy
- */
- publicvoidonDrop(intx,inty){
- DragGridAdapteradapter=(DragGridAdapter)getAdapter();
- adapter.setIsHidePosition(-1);//不進行隱藏
- }
- }
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?- gv_main.setOnItemClickListener(newOnItemClickListener(){
- @Override
- publicvoidonItemClick(AdapterView<?>parent,Viewview,
- intposition,longid){
- gv_main.setDoTouch(false);
- Toast.makeText(MainActivity.this,
- adapter.getItemId(position)+"",1).show();
- }
- });
- gv_main.setOnItemLongClickListener(newOnItemLongClickListener(){
- @Override
- publicbooleanonItemLongClick(AdapterView<?>parent,Viewview,
- intposition,longid){
- gv_main.setDoTouch(true);
- returntrue;
- }
- });
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.網上有寫應該就是這麼搞的,具體實現不知道,不過這種方案可以實現,有時間接著續.
推薦閱讀: