android ListView和GridView拖拽移位实现代码

关于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);// 不进行隐藏

}

}

(0)

相关推荐

  • Android开发之ListView、GridView 详解及示例代码

    ListView与GridView是Android开发中的常用控件,它们和Adapter配合使用能够实现很多界面效果.下面分别以实例说明ListView.GridView的用法.        1.ListView的Android开发实例 ListView 是android开发中最常用的控件之一,一般构成列表包括三个元素,ListView:用来展示列表的视图.Adapter:数据与视图连接的桥梁.Data:具体的数据包括字符串 .图片或者控件. 适配器一般有以下几种类型: ArrayAdapte

  • Android 中ListView和GridView赋值错位

    ListView和GridView多次调用getView的bug,导致赋值错位 最近总遇到写GridView适配器赋值时,最后一两个需定义其他图片时,赋图错误,原因就是适配器的getView多次调用,导致数据赋值错误,上网搜索终于找到解决方案,以此记录下. 一.ListView 1.原因 因为listView一般用的是wrap_content,高度不确定,导致系统需要不断地测量,也就多次调用onMeasure方法,所以就多次调用getView. 2.解决 很简单,把宽高写死即可(明确给个数字或者

  • Android 中ScrollView嵌套GridView,ListView的实例

    Android 中ScrollView嵌套GridView,ListView的实例 在Android开发中,经常有一些UI需要进行固定style的动态布局,然而由于现在的UI都喜欢把一个界面拉的很长,所以我们很多情况下需要使用ScrollView来嵌套列表控件来实现UI.这样就导致了很多不顺心的问题. 问题一:列表控件显示不完全 原因是嵌套情况下,ScrollView不能正确的计算列表控件的高度. 有两种解决方案 方案一 在适配器赋值完成后代码动态计算列表的高度.这里贴出ListView的计算代

  • Android仿贴吧内容下的简单ListView嵌套GridView

    ListView嵌套GridView的简单实例 我的项目想实现一个listview里面的每个item都嵌套一个GridView,顶部还有主题等内容,如 总所周知,关于ListView嵌套GridView,最主要问题莫过于嵌套状态下滑动冲突问题,具体怎么解决,喜欢冗长无注释的代码的,请点击这里这篇文章跟其他的都大同小异了,不过在缺少注释的情况下,我发现了一点点小问题: /** * 创建日期:2017/3/21. * 说明:构造方法会根据你的SDK最低版本不同而要求不同,如18的至少必须重写前 *

  • 关于Android中ListView嵌套GridView的问题

    问题 在Android开发中,遇到一个问题,是ListView嵌套GridView,需要点击整个ListView的Item进行跳转.但是在点击GridView区域时无法进行页面的跳转.这是因为GridView获得了焦点.导致点击无法跳转. 解决方法就是: 1.在Item最外层加上 android:descendantFocusability="blocksDescendants" 2.在Adapter中添加 holder.mGridView.setClickable(false); h

  • Android之ScrollView嵌套ListView和GridView冲突的解决方法

    那么里面的ScrollView高度计算就会出现问题.我们也就无法得到想要的效果.核心解决方案: 重写ListView或者GridView的OnMesure 方法. 复制代码 代码如下: public class MyListView extends ListView {        public MyListView(Context context) {                super(context);        }        public MyListView(Conte

  • Android RecyclerView详解之实现 ListView GridView瀑布流效果

     什么是RecyclerView RecyclerView 是Google推出的最新的 替代ListView.GridView的组件,RecyclerView是用来显示大量数据的容器,并通过有限数量的子View,来提高滚动时的性能. 与ListView不同,RecyclerView 不再负责布局,而是专注于布局复用.布局主要通过 LayoutManager来管理,目前提供了3种常用的布局管理: LinearLayoutManager 线性布局管理器 (ListView效果) GridLayout

  • Android ScrollView 下嵌套 ListView 或 GridView出现问题解决办法

    Android ScrollView 下嵌套 ListView 或 GridView出现问题解决办法 ScrollView 下嵌套 ListView 或 GridView 会发列表现数据只能显示一行.因为他们都是滚动结构,两个滚动条放到一起就会引起冲突. 解决此问题可以通过计算 ListView 高度或重写 ListView 的 onMeasure 方法来解决.下面介绍通过重写 onMeasure 方法来解决问题. 重写 onMeasure 方法如下: public class ScrollLi

  • android ListView和GridView拖拽移位实现代码

    关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:             首先说一下:拖拽ListView的item就不应该可以任意移动,只应该在ListView所在的范围内,而网易的你看看我都可以移动到状态栏了,虽然你做了处理,但是用户体验我个人感觉不好,在看看百度的,不仅控制了移动范围,更不错的百度的移动起来会时时的换位,看起来相当的形象,所以我认为这样相当的棒.说明一点

  • JS实现网站菜单拖拽移位效果的方法

    本文实例讲述了JS实现网站菜单拖拽移位效果的方法.分享给大家供大家参考.具体如下: 这是一个基于JavaScript的层手动实例,让网站的菜单可以拖拽移位,记得土豆网的"豆单"有这种功能.本效果还尚未彻底完成,部分地方因没有写入对应内容,因此JS可能会提示有错误. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-web-menu-tzyw-style-codes/ 具体代码如下: <html> <head>

  • Android TouchListener实现拖拽删实例代码

    Android TouchListener实现拖拽删实例代码 如果为一个控件设置了该触摸监听, 控件会随着用户的拖动而移动, 如果拖动的距离大过设置的临界值, 那么当松开手指时会有回调onDragComplete, 用户可在该方法中将该控件从父布局中删除, 或这进行其他操作. 如果用户拖拽的距离小于临界值, 那么当用户松开手指时控件会回谈到原来的初始位置.这时会触发onDragRebound回调. 如果用户触摸控件之后没有拖拽而是直接松开手指, 会触发onClick回调, 这样用户就不用为该控件

  • android的RecyclerView实现拖拽排序和侧滑删除示例

    在平时开发应用的时候,经常会遇到列表排序.滑动删除的需求.如果列表效果采用的是 ListView 的话,需要经过自定义 View 才能实现效果:但是如果采用的是 RecyclerView 的话,系统 API 就已经为我们提供了相应的功能. 接下来,我们就来看一下怎么用系统 API 来实现排序和删除的效果. 创建 ItemTouchHelper 创建一个 ItemTouchHelper 对象,然后其调用 attachToRecyclerView 方法: RecyclerView recyclerV

  • Android仿qq消息拖拽效果

    本文实例为大家分享了Android仿qq消息拖拽效果展示的具体代码,供大家参考,具体内容如下 这是一个仿qq消息拖拽效果,View和拖拽实现了分离,TextView.Button.Imageview等都可以实现相应的拖拽效果:在触发的地方调用 MessageBubbleView.attach(findViewById(R.id.text_view), new MessageBubbleView.BubbleDisappearListener() { @Override public void d

  • Android自定义View实现拖拽效果

    腾讯QQ有那种红点拖动效果,今天就来实现一个简单的自定义View拖动效果,再回到原处,并非完全仿QQ红点拖动. 先来看一下效果图 简单说一下实现步骤 1.创建一个类继承View 2.绘制出一个小球 3.重写onTouchEvent,来根据手指放下,移动,抬起,来控制小球 4.直接在布局中引用 先贴一张图看下View的坐标系 下面就贴一下代码,最后会给出源码 public class CustomView extends View { private int lastX; private int

  • Android实现悬浮可拖拽的Button

    本文实例为大家分享了Android实现悬浮可拖拽Button的具体代码,供大家参考,具体内容如下 1.简介 最近,因为项目需要,需要制作一个界面上可拖拽的按钮,网上也有多实例,看了下大部分都是示例不全或讲解不清晰,效果图也不明显,借此自己记录一番自己的实现方案,以备不时之需,同时也为广大学者可以直接通过拷贝方式完成项目所需. 2.效果图 在开始代码之前,首先看看效果图,如下: 3.核心代码实现 3.1 DraggingButton 实现 public class DraggingButton e

  • Android实现View的拖拽

    本文实例为大家分享了Android实现View拖拽的具体代码,供大家参考,具体内容如下 前言 实现View的拖拽,其实原理很简单.无非就是获取手指的位移信息,然后view根据手指的位移信息,移动对应的位置. 首先是获取手机的位移信息就可以根据需求不同分为两种 拖拽view本身,view实现移动.则设置view的setOnTouchListener. 在activity中随意滑动,view都会反应出动作.则重写activity onTouchEvent方法. 而移动的方法嘛,也有几种 给view设

  • Android 实现仿QQ拖拽气泡效果的示例

    目录 效果图: 一.实现思路 二.功能实现 三.全屏拖拽效果实现 源码地址: 效果图: 一.实现思路 在列表中默认使用自定义的TextView控件来展示消息气泡,在自定义的TextView控件中重写onTouchEvent方法,然后在DOWN.MOVE.UP事件中分别处理拖拽效果. 整个拖拽效果我们可以拆分成以下几步来实现: 1.默认状态 2.两气泡相连状态 3.两气泡分离状态 4.气泡消失状态 二.功能实现 默认状态:用来做一个状态的标识,无需特别处理. 两气泡相连状态:绘制一个固定圆和一个移

随机推荐