android ListView和GridView拖拽移位具体实现及拓展
关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:
首先说一下:拖拽ListView的item就不应该可以任意移动,只应该在
ListView所在的范围内,而网易的你看看我都可以移动到状态栏了,虽然你做了处理,但是用户体验我个人感觉不好,在看看百度的,不仅控制了移动范
围,更不错的百度的移动起来会时时的换位,看起来相当的形象,所以我认为这样相当的棒.
说明一点,我没有那么有才,我也是看别人代码,然后自己整理下.在这里就简单记载一下.
首先对touch事件的处理,从应用中,我们可以得出,在我们点击后面拖拉图标后,就会创建一个item的影像视图.并且可以移动该影像,而此时的ListView不应该有touch事件.
onInterceptTouchEvent方法.
- /***
- * 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事件:
- /**
- * 触摸事件处理
- */
- @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事件.
大致就介绍这么多,具体的实现,还是大家看源码吧,我注释的还算清晰,只要大家仔细看的话,一定可以掌握的,为什么这么说呢,技术只有在掌握了情况下才可以进行拓展.
对了,提醒大家要理解这三句话:
坐标(这两个值不会超过view的长度和宽度)。
- 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);
- }
- }
- }
下面我说下适配器:
- /***
- * 自定义适配器
- *
- * @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.并且除了第一次移动外,在每次交换后要除去映射源的显示,这样用户觉得这里的空位就是就是为我准备的,比较人性化.
实现起来并不复杂,前提是你得掌握上面的操作.
源码如下;
- 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一样。
原理也一样.下面我们直接看代码吧.
- 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);// 不进行隐藏
- }
- }
相信大家只要ListView拖拽弄白后,这个GridView也会轻易弄出来,其实拖拽就是对坐标的考察。
向大家展示一下效果:
但是有个不足的地方,网上一些例子都是长按可以拖拽,而点击则执行点击事件.其实实现起来也不是很复杂,可是在实现的过程中,遇到了诡异纠结的问题,郁闷了一天,结果目前先放弃,以后哪天在搞搞吧.纠结的问题就是错位.
我说下我的思路:首先,我们在自定义GridView中创建一个控制是否可以Touch拖拽的变量,而这个变量的值我们通过对GridView的setOnItemClickListener和setOnItemLongClickListener来获取,
如:
- gv_main.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- gv_main.setDoTouch(false);
- Toast.makeText(MainActivity.this,
- adapter.getItemId(position) + "", 1).show();
- }
- });
- gv_main.setOnItemLongClickListener(new OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view,
- int position, long id) {
- gv_main.setDoTouch(true);
- return true;
- }
- });
这样我们就实现了长按可以拖拽的效果了,可是遇到个变态的问题,不过这个思路没有错,肯定可以实现.就
先说到这里,其实通过这个例子,我们还可以拓展实现ListView上滑动的时候,到达Title时,Title停留在顶部,当下一个Titile滑动到
这里的时候,那么代替前面那个TItle.网上有写应该就是这么搞的,具体实现不知道,不过这种方案可以实现,有时间接着续.
android ListView和GridView拖拽移位具体实现及拓展的更多相关文章
- android ListView和GridView拖拽移位实现代码
关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例: 首先 ...
- Android中GridView拖拽的效果【android进化三十六】
最 近看到联想,摩托罗拉等,手机launcher中有个效果,进入mainmenu后,里面的应用程序的图标可以拖来拖去,所以我也参照网上给的代码,写了 一个例子.还是很有趣的,实现的流畅度没有人家的 ...
- Android中GridView拖拽的效果
最 近看到联想,摩托罗拉等,手机launcher中有个效果,进入mainmenu后,里面的应用程序的图标可以拖来拖去,所以我也参照网上给的代码,写了 一个例子.还是很有趣的,实现的流畅度没有人家的那么 ...
- Android listview和gridview以及view的区别
GridView 可以指定显示的条目的列数. listview一般显示的条目的列数都是一列 如果是列表(单列多行形式)的使用ListView,如果是多行多列网状形式的优先使用GridView andr ...
- android listView嵌套gridview的使用心得
在开发的过程中可能需要用到listview嵌套gridview的场景,但是在Android中, 不能在一个拥有Scrollbar的组件中嵌入另一个拥有Scrollbar的组件,因为这不科学,会混淆滑动 ...
- Android 自动化测试—robotium(八) 拖拽
本文来源于:http://xiaomaozi.blog.51cto.com/925779/933056 SeekBar控件 代码实现:http://luwenjie.blog.51cto.com/92 ...
- android Listview,gridview局部刷新,部分刷新
众所周知Listview和Gridview的刷新界面的方式是调用adapter.notifyDataSetChanged()进行界面刷新. 但是此方法有其弊端,他是将界面中的数据全部刷新一遍,不论数据 ...
- Android -- 实现RecyclerView可拖拽Item
1,今天和大家一起实现RecyclerView可拖拽Item,主要是使用RecyclerView结合ItemTouchHelper来实现的,来看一下效果 2,看一下怎么实现的呢,很简单,只需要给rec ...
- ListView 多行拖拽排序
核心代码:修改ListView的属性,及绑定事件 // 初始化listView1. private void InitializeListView() { listView1.AllowDrop = ...
随机推荐
- Nginx反向代理配置配置实例
为了节省支出,公司需要将分布在不同机器的站点都迁移到一台机器,而目前不同机器运行的是不同的web服务,部分是nginx,部分是apache,由于牵涉较多rewrite规则,为了节省修改功夫,打算迁移后 ...
- 四、C#方法和参数
方法是一种组合一系列语句以执行一个特定操作或计算一个特殊结果的方式. 它能够为构成程序的语句提供更好的结构和组织. 在面向对象的语言中,方法总是和类关联在一起的,我们用类将相关的方法分为一组. 方 ...
- C# 深入浅出 委托与事件
C#中的委托和事件的概念接触很久了,但是一直以来总没有特别透彻的感觉,现在我在这里总结一下: 首先我们要知道委托的由来,为什么要使用委托了? 我们先看一个例子: 假设我们有这样一个需求,需要计算在不同 ...
- Hibernate中分页
query.setFirstResult(4);query.setMaxResults(5); 这两个方法就是hibernate的分页
- gdb调试 使用心得
1: 对于在应用程序中加入参数进行调试的方法: 直接用 gdb app -p1 -p2 这样进行调试是不行的. 需要像以下这样使用: #gdb app (gdb) r -p1 -p ...
- 一元云购完整源码 云购CMS系统 带安卓和ios手机客户端
看起来不错的一套一元云购CMS源码,源码包里面带了安卓和ios手机客户端,手机客户端需要自己反编译. 这里不做功能和其它更多的介绍,可以自己下载后慢慢测试了解. 下面演示图为亲测截图< ...
- DeDe友情链接
图片 {dede:flink row="} [field:link /] {/dede:flink} 文字 {dede:flink row="} [field:link /] {/ ...
- window.onload() 等待所有的数据加载都完成之后才会触发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Delphi-LowerCase 函数
函数名称 LowerCase 所在单元 System.SysUtils 函数原型 function LowerCase(const S: string): string; 函数功能 将字符串中所有的大 ...
- 平稳退化,JS和HTML标记分离,极致性能的JavaScript图片库
index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...