編輯:Android開發實例
關於ListView拖拽移動位置,想必大家並不陌生,比較不錯的軟件都用到如此功能了.如:搜狐,網易,百度等,但是相比來說還是百度的用戶體驗較好,不偏心了,下面看幾個示例:
首先說一下:拖拽ListView的item就不應該可以任意移動,只應該在ListView所在的范圍內,而網易的你看看我都可以移動到狀態欄了,雖然你做了處理,但是用戶體驗我個人感覺不好,在看看百度的,不僅控制了移動范圍,更不錯的百度的移動起來會時時的換位,看起來相當的形象,所以我認為這樣相當的棒.
說明一點,我沒有那麼有才,我也是看別人代碼,然後自己整理下.在這裡就簡單記載一下.
首先對touch事件的處理,從應用中,我們可以得出,在我們點擊後面拖拉圖標後,就會創建一個item的影像視圖.並且可以移動該影像,而此時的ListView不應該有touch事件.
onInterceptTouchEvent方法.
[java]
代碼如下:
/***
* 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]
代碼如下:
/**
* 觸摸事件處理
*/
@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 , getTop, getBottom,getRight, 這個指的是該控件相對於父控件的距離.
源碼:
[java]
代碼如下:
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]
代碼如下:
/***
* 自定義適配器
*
* @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]
代碼如下:
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]
代碼如下:
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);// 不進行隱藏
}
}
本文實例介紹的Android的Gallery控件是個很不錯的看圖控件,可以大大減輕開發者對於看圖功能的開發,並且效果也很美觀。本文實例中的Gallery的用法,主
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
shimmer開源庫介紹 Shimmer是Fa
在4.5.6節介紹過一個<include>標簽,該標簽可以在布局文件中引用另外一個布局文件,並可以覆蓋被引用布局文件根節點所有與布局相關的屬性,也就是