项目 中要用到拖动排序的效果,于是百度到网上的做法,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;
}
}
}