转载请注明出处:http://blog.csdn.net/ganklun/article/details/43448729
最近一个项目中要求用到交换任意两个ListView里面Item位置,需要实现这个功能。因此,本屌也是通过学习相关例子,重绘封装了一番,并且实现了这个功能。下面,就一起来看看是怎么样实现的吧。
1、XML布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical" >
<com.czl.struct.widget.DragListView
android:id="@+id/dragLvi"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:cacheColorHint="@android:color/transparent"
android:divider="#DCD7CF"
android:dividerHeight="1dp"
android:fadingEdgeLength="0dip"
android:focusable="false"
android:focusableInTouchMode="false"
android:listSelector="@android:color/transparent"
android:scrollbars="vertical"
android:typeface="sans" >
</com.czl.struct.widget.DragListView>
</LinearLayout>
是不是和平时引入ListView没有任何区别呢,只不过把LIstView换成了我们自定义的DragListView。
2、自定义DragListView
现在我们需要实现我们自定义的DragListView,并且实现相关交换位置的接口即可。先看一下需要实现的接口,代码如下所示:
public interface DragItemChangeListener {
public void onDragItemChange(int dragSrcPosition,int dragPosition);
}
是不是很简单啊,我们只需要为DragListView设置这个拖拽事件监听即可。该事件回调方法看名字也很容易理解,就是当位置被交换时,做相应回调处理即可,其中第一个参数dragSrcPosition是交换前的位置,dragPosition是交换后的位置。接下来贴出DragListView的代码:
public class DragListView extends ListView {
private WindowManager windowManager; // windows窗口控制类
private WindowManager.LayoutParams windowParams; // 用于控制拖拽项的显示的参数
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 dragImageSourceId;
private DragItemChangeListener dragItemChangeListener;
/***
* 构造方法
*
* @param context
* @param attrs
*/
public DragListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setDragImageSourceId(int dragImageSourceId) {
this.dragImageSourceId = dragImageSourceId;
}
public void setDragItemChangeListener(DragItemChangeListener dragItemChangeListener) {
this.dragItemChangeListener = dragItemChangeListener;
}
/***
* 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(dragImageSourceId);
// 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(Context.WINDOW_SERVICE);
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) {
// 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()) {
dragItemChangeListener.onDragItemChange(dragSrcPosition, dragPosition);
}
}
}
其中setDragItemChangeListener(DragItemChangeListener dragItemChangeListener) 就是我们要注入接口实现的地方。setDragImageSourceId(int dragImageSourceId) 方法可以设置您想拖拽的图标资源。接下来万事俱备,只欠东风了。
3、引入DragListView
<pre name="code" class="java">废话不说,直接上代码:
<pre name="code" class="java">public class DragListActivity extends BaseActivity implements DragItemChangeListener {
private DragListView dragListView;
private ArrayList<HashMap<String, Object>> list;
private DemoAdapter demoAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_list);
init();
findViewById();
}
@Override
public void init() {
list = new ArrayList<HashMap<String, Object>>();
for (int i = 0; i < 20; i++) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("numbTV", "D138026594664912200" + i);
map.put("adrTv", "浙江省杭州市西湖区小和山");
map.put("timeTv", "2013-09-27 15:23");
map.put("callTv", "18778900578");
list.add(map);
}
}
@Override
public void findViewById() {
dragListView = (DragListView) findViewById(R.id.dragLvi);
demoAdapter = new DemoAdapter(DragListActivity.this, list);
dragListView.setAdapter(demoAdapter);
dragListView.setDragImageSourceId(R.id.imageView1);
dragListView.setDragItemChangeListener(this);
}
@Override
public void Message(android.os.Message msg) {
}
@Override
public void onDragItemChange(int dragSrcPosition, int dragPosition) {
HashMap<String, Object> map = list.get(dragSrcPosition);
list.remove(dragSrcPosition);
list.add(dragPosition, map);
demoAdapter.notifyDataSetChanged();
}
}
适配器就和平时使用的BaseAdapter没有任何区别,这里就不再赘述了,首先我们初始化了20条数据,然后我们重点关注setDragImageSourceId()和setItemChangeListener()这两行代码,聪明的你肯定已经完全明白了吧,这里就是我们设置自定义拖拽图标以及添加交换位置监听的实现。onDragItemChange()就是我们回调后具体交换位置刷新UI界面的代码啦。
4、下载地址
https://github.com/GankLun/DragListView
5、总结
总的来说,要实现ListView交换Item项位置,只需三步,第一,XML布局文件里引入我们自定义的DragListView,第二,为dragListView设置拖拽图片资源和事件触发监听,第三,实现onDragItemChange交换位置接口,进行位置交换并且刷新UI界面。很简单,有木有?大家都来试一下吧。