1354182517_7951  

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

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

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

onInterceptTouchEvent方法.

 

/***
	 * 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事件:

 

/**
	 * 觸摸事件處理
	 */
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// item的view不为空,且獲取的dragPosition有效
		if (dragImageView != null && dragPosition != INVALID_POSITION) {
			int action = ev.getAction();
			switch (action) {
			case MotionEvent.ACTION_UP:
				int upY = (int) ev.getY();
				stopDrag();
				onDrop(upY);
				break;
			case MotionEvent.ACTION_MOVE:
				int moveY = (int) ev.getY();
				onDrag(moveY);

				break;
			case MotionEvent.ACTION_DOWN:
				break;
			default:
				break;
			}
			return true;// 取消ListView滑動.
		}

		return super.onTouchEvent(ev);
	}

簡單說明:首先在Touch中,我們要進行判斷,是否點擊的是拖動圖標,如果是的話,那麼對ACTION_MOVE and ACTION_UP相應事件進行處理,並且返回true or false.作用:取消ListView自身的Touch事件.如果不是的話,執行ListView 本身的Touch事件.

 

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

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

 

getRawX()和getRawY():獲得的是相對屏幕的位置.
getX()和getY():獲得的永遠是相對view的觸摸位置 坐標這兩個值不會超過view的長度和寬度)。
getLeft getTopgetBottom,getRight這個指的是該控件相對於父控件的距離.
源碼:

 

 

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);
		}

	}

}

下面我說下适配器:

 

 

/***
	 * 自定義适配器
	 * 
	 * @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;
		}

	}

 

這裏不過多解釋了,相信大家都看的明白.如果疑問請留言.

展示下運行效果:

1354206807_6198  

效果看起來還行吧,如果覺得不錯的話,記得要贊一個哦.

 

下面我們接着修改,模擬百度嘛,誰讓百度這麼牛叉呢.

思路:點中拖拉圖標的時候,每次移動只要dragPosition發生改變,也就是我移動到了下一個位置,那麼此時我就進行交換執行update.並且除了第一次移動外,在每次交換後要除去映射源的顯示,這样用戶覺得這裏的空位就是就是为我准備的,比較人性化.

實現起來並不复雜,前提是你得掌握上面的操作.

源碼如下;

 

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進行隱藏之類的操作,這些代碼我就不寫了,我會將案例上傳網上,不懂的可以下載源碼.

好了還是我們來觀看下效果吧.

1354244742_6656  

怎麼样,這個效果看起來要比上面那個效果更人性化點吧,我的操作或許有點快,不信的話,你自己手機體驗一下吧.

關於ListView拖拽就說到這裏,如有不足請大家自己創新.

 

下面我們接着對GridView的拖拽簡單說明.因为這些在項目中我們都會用到,所以既然做到就做全面點吧.好了大家接着往下看吧.

首先說明,原理一样,都是拖動映像,記錄拖動位置,然後調用notifyDataSetChanged更新UI.

而GridView不同的是你要根據x,y值共同獲取點擊的position和移動至的position,而ListView因为不涉及x坐標.

嗯,最初的原始移動我就不给大家展示了,效果也不是很友好,我直接展示時時更新的那種方法.效果類是與上面那個時時更新ListView一样。

原理也一样.下面我們直接看代碼吧.

 

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也會輕易弄出來,其實拖拽就是對坐標的考察。

 

向大家展示一下效果:

1354518890_6212  

但是有個不足的地方,網上一些例子都是長按可以拖拽,而點擊則執行點擊事件.其實實現起來也不是很复雜,可是在實現的過程中,遇到了詭異糾結的問題,鬱悶了一天,結果目前先放棄,以後哪天在搞搞吧.糾結的問題就是錯位.

我說下我的思路:首先,我們在自定義GridView中創建一個控制是否可以Touch拖拽的變量,而這個變量的值我們通過對GridView的setOnItemClickListener和setOnItemLongClickListener來獲取,

如:

 

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

 


From:CSDN        

 




Posted by shadow at 痞客邦 PIXNET 留言(0) 引用(0) 人氣()