實現可拖動排序的ListView-DragListView
阿新 • • 發佈:2019-01-26
專案 中要用到拖動排序的效果,於是百度到網上的做法,github上開源框架被我pass, 為了一個小功能匯入一庫太不划算。然後看到這篇
http://blog.csdn.net/jj120522/article/details/8240407,可能是博主原始碼給的不全,看到好多人要原始碼。其實看懂後,很容易的就能補全差的東西。既然用到,自己一定要去實現一遍,只有這樣才能真正學到。我對這個控制元件做了些整理和優化, 這樣使用時和普通的ListView就沒有什麼區別了。
實現步驟:
1. 按下時, 創建出item的虛影。
2. 拖動時, 移動虛影,同步進行item的交換和listview的上下滑動
3. 放下時, 移除虛影, 顯示出當前的item.優化:
1. 移動時item的隱藏, 這樣adapter中就不用去處理隱藏問題
2. itemView.setDrawingCacheEnabled 方法 開啟cache ,建立bitmap後一定要關閉cache, 這樣解決拖動時的錯亂問題,item就可以 複用了。
3. 上下出界問題
下面是DragListView的原始碼
public class DragListView extends ListView {
private int dragViewId;
private WindowManager.LayoutParams windowParams;
private WindowManager windowManager;
private ImageView dragImageView;
private int offsetScreenTop; //距離螢幕頂部的位置
private int offsetViewTop; //手指按下位置距離item頂部的位置
private int dragPosition;
private int srcY; //用於判斷滑動方向
public DragListView(Context context) {
super(context);
}
public DragListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setDragViewId(int dragViewId) {
this.dragViewId = dragViewId;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
int x = (int) ev.getX();
int y = (int) ev.getY();
int rawY = (int) ev.getRawY();
int currentPostion = dragPosition = pointToPosition(x, y);
if (currentPostion == AdapterView.INVALID_POSITION) {
return super.onInterceptTouchEvent(ev);
}
//getChildAt是獲取可見位置的item
ViewGroup itemView = (ViewGroup) getChildAt(currentPostion - getFirstVisiblePosition());
offsetScreenTop = rawY - y;
offsetViewTop = y - itemView.getTop();
// 獲取可拖拽的圖示
View dragger = itemView.findViewById(dragViewId);
if (dragger != null && x > dragger.getLeft()) {
itemView.setDrawingCacheEnabled(true);// 開啟cache.
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根據cache建立一個新的bitmap物件.
itemView.setDrawingCacheEnabled(false);// 一定關閉cache,否則複用會出現錯亂
startDrag(bm, y);
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (dragImageView != null) {
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
srcY = y;
break;
case MotionEvent.ACTION_MOVE:
onDrag(y);
getChildAt(dragPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
break;
case MotionEvent.ACTION_UP:
stopDrag();
getChildAt(dragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);
break;
}
return true;
}
return super.onTouchEvent(ev);
}
private void startDrag(Bitmap bm, int y) {
/***
* 初始化window.
*/
windowParams = new WindowManager.LayoutParams();
windowParams.gravity = Gravity.TOP;
windowParams.x = 0;
windowParams.y = y - offsetViewTop + offsetScreenTop;
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(Context.WINDOW_SERVICE);
windowManager.addView(imageView, windowParams);
dragImageView = imageView;
}
/**
* 拖動景象
*
* @param y
*/
private void onDrag(int y) {
int offsetTop = y - offsetViewTop; //頂部不能出界
if (dragImageView != null && offsetTop >= 0 && offsetTop <= getChildAt(getChildCount() - 1).getTop()) {
windowParams.alpha = 0.8f;// 透明度
windowParams.y = y - offsetViewTop + offsetScreenTop;// 移動y值.//記得要加上dragOffset,windowManager計算的是整個螢幕.(標題欄和狀態列都要算上)
windowManager.updateViewLayout(dragImageView, windowParams);// 時時移動.
}
onChange(y);
scrollListView(y);
}
/**
* 同步滑動ListView
* @param y
*/
private void scrollListView(int y) {
View view = getChildAt(dragPosition - getFirstVisiblePosition());
int offsetY = srcY - y;
if (y < getHeight() / 3 && y < srcY) { //listview向上滑
setSelectionFromTop(dragPosition, offsetY + view.getTop());
} else if (y > getHeight() / 3 * 2 && y > srcY) { //listview向下滑
setSelectionFromTop(dragPosition, offsetY + view.getTop());
}
srcY = y;
}
/**
* 同步改變item的位置
* @param y
*/
private void onChange(int y) {
int currentPostion = pointToPosition(0, y);
if (currentPostion == AdapterView.INVALID_POSITION) {
currentPostion = dragPosition;
}
if (dragPosition != currentPostion) {
DragAdapter adapter = (DragAdapter) getAdapter();
adapter.change(dragPosition, currentPostion);
swich(dragPosition, currentPostion);
}
dragPosition = currentPostion;
}
/***
* 切換隱藏的位置
*/
private void swich(int start, int end) {
getChildAt(start - getFirstVisiblePosition()).setVisibility(View.VISIBLE);
getChildAt(end - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
}
/**
* 停止拖動,刪除影像
*/
public void stopDrag() {
if (dragImageView != null) {
windowManager.removeView(dragImageView);
dragImageView = null;
}
}
}
這樣實現DragAdapter 只用實現一個update方法
public abstract class DragAdapter extends BaseAdapter {
private List<String> data;
public DragAdapter(List<String> data) {
this.data = data;
}
/**
* 交換位置
*
* @param start
* @param end
*/
public void change(int start, int end) {
String srcData = data.get(start);
data.remove(srcData);
data.add(end, srcData);
notifyDataSetChanged();
}
}
在Activity中使用除了要設定一個拖動按鈕的id, 其它和ListView沒有任何區別了
public class DragListViewActivity extends Activity {
private DragListView dragListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_list_view);
dragListView = (DragListView) findViewById(R.id.dragListView);
dragListView.setDragViewId(R.id.tvDrag);
List<String> data = initData();
MyAdapter dragAdapter = new MyAdapter(data,this);
dragListView.setAdapter(dragAdapter);
}
private List<String> initData() {
List<String> data = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
data.add("標題xxx" + i);
}
return data;
}
private class MyAdapter extends DragAdapter {
private List<String> data;
private Context context;
public MyAdapter(List<String> data, Context context) {
super(data);
this.data = data;
this.context = context;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder = null;
if (convertView == null) {
holder = new Holder();
convertView = View.inflate(context, R.layout.list_view_item, null);
holder.tvDrag = (TextView) convertView.findViewById(R.id.tvDrag);
holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.tvName.setText(data.get(position));
return convertView;
}
private class Holder {
TextView tvName;
TextView tvDrag;
}
}
}