赞
踩
上篇博客分享了一个实现ListView中item交换动画的控件(戳这里查看),但是有些情况下我们的需求比这种效果要复杂。比如说需要手动拖拽item来完成item交换的交互。像这样:
还有这样:
这次分享的控件就实现这样的功能,下面开始正文。
先说实现item拖拽功能的DragListView。上代码:
- public class DragListView extends ListView {
-
- /**
- * 速度模板,影响视图移动时的速度变化
- * <p>
- * MODE_LINEAR // 线性变化模式
- * MODE_ACCELERATE // 加速模式
- * MODE_DECELERATE // 减速模式
- * MODE_ACCELERATE_DECELERATE // 先加速后加速模式
- */
- public static final int MODE_LINEAR = 0x001;
- public static final int MODE_ACCELERATE = 0x002;
- public static final int MODE_DECELERATE = 0x003;
- public static final int MODE_ACCELERATE_DECELERATE = 0x004;
-
- private Context context;
-
- // 拖动时的视图
- private View dragView;
-
-
- private WindowManager windowManager;
- private WindowManager.LayoutParams windowLayoutParams;
- private BaseDragAdapter adapter;
-
-
- /**
- * 可设置选项
- */
- // 移动动画储持续时间,单位毫秒
- private long duration = 300;
-
- // 速度模板
- private int speedMode = MODE_ACCELERATE_DECELERATE;
-
- // 自动滚动的速度
- private int scrollSpeed = 50;
-
-
- /**
- * 运行参数
- */
- // 拖动块的原始坐标
- private int originalPosition = -1;
-
- // 拖动块当前所在坐标
- private int currentPosition = -1;
-
- // 用于记录上次点击事件MotionEvent.getX();
- private int lastX;
-
- // 用于记录上次点击事件MotionEvent.getY();
- private int lastY;
-
- // 用于记录上次点击事件MotionEvent.getRawX();
- private int lastRawX;
-
- // 用于记录上次点击事件MotionEvent.getRawY();
- private int lastRawY;
-
- // 拖动块中心点x坐标,用于判断拖动块所处的列表位置
- private int dragCenterX;
-
- // 拖动块中心点y坐标,用于判断拖动块所处的列表位置
- private int dragCenterY;
-
- // 滑动上边界,拖动块中心超过该边界时列表自动向下滑动
- private int upScrollBorder;
-
- // 滑动下边界,拖动块中心超过该边界时列表自动向上滑动
- private int downScrollBorder;
-
- // 状态栏高度
- private int statusHeight;
-
- // 拖动时的列表刷新标识符
- private boolean dragRefresh;
-
- // 拖动锁定标记,为false时选中块可被拖动
- private boolean dragLock = true;
-
- // 动画列表,存放当前屏幕上正在播放的所有滑动动画的动画对象
- private ArrayList<Animator> animatorList;
-
- // 视图列表,存放当前屏幕上正在播放的所有滑动动画的视图对象
- private ArrayList<View> dragViews;
-
-
- /**
- * 可监听接口
- */
- // 拖动块视图对象生成器,可通过设置该接口自定义一个拖动视图的样式,不设置时会有默认实现
- private DragViewCreator dragViewCreator;
-
- // 拖动监听接口,拖动开始和结束时会在该接口回调
- private OnDragingListener dragingListener;
-
- // 当前拖动目标位置改变时,每次改变都会在该接口回调
- private OnDragTargetChangedListener targetChangedListener;
-
-
- // 内部接口,动画观察者,滑动动画结束是回调
- private AnimatorObserver animatorObserver;
-
- private Handler handler = new Handler();
-
- // 列表自动滚动线程
- private Runnable scrollRunnable = new Runnable() {
- @Override
- public void run() {
- int scrollY;
-
- // 滚动到顶或到底时停止滚动
- if (getFirstVisiblePosition() == 0 || getLastVisiblePosition() == getCount() - 1) {
- handler.removeCallbacks(scrollRunnable);
- }
-
- // 触控点y坐标超过上边界时,出发列表自动向下滚动
- if (lastY > upScrollBorder) {
- scrollY = scrollSpeed;
- handler.postDelayed(scrollRunnable, 25);
- }
- // 触控点y坐标超过下边界时,出发列表自动向上滚动
- else if (lastY < downScrollBorder) {
- scrollY = -scrollSpeed;
- handler.postDelayed(scrollRunnable, 25);
- }
- // // 触控点y坐标处于上下边界之间时,停止滚动
- else {
- scrollY = 0;
- handler.removeCallbacks(scrollRunnable);
- }
-
- smoothScrollBy(scrollY, 10);
- }
- };
-
- public DragListView(Context context) {
- super(context);
- init(context);
- }
-
- public DragListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public DragListView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
-
- /**
- * 初始化方法
- *
- * @param context
- */
- private void init(Context context) {
- this.context = context;
- statusHeight = getStatusHeight();
- windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- animatorList = new ArrayList<>();
- dragViews = new ArrayList<>();
-
- // 拖动块视图对象生成器的默认实现,返回一个与被拖动项外观一致的ImageView
- dragViewCreator = new DragViewCreator() {
- @Override
- public View createDragView(int width, int height, Bitmap viewCache) {
- ImageView imageView = new ImageView(DragListView.this.context);
- imageView.setImageBitmap(viewCache);
-
- return imageView;
- }
- };
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent motionEvent) {
-
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN:
-
- downScrollBorder = getHeight() / 5;
- upScrollBorder = getHeight() * 4 / 5;
-
- // 手指按下时记录相关坐标
- lastX = (int) motionEvent.getX();
- lastY = (int) motionEvent.getY();
- lastRawX = (int) motionEvent.getRawX();
- lastRawY = (int) motionEvent.getRawY();
-
- currentPosition = pointToPosition(lastRawX, lastRawY);
-
- if (currentPosition == AdapterView.INVALID_POSITION || !adapter.isDragAvailable(currentPosition)) {
- return true;
- }
-
- originalPosition = currentPosition;
-
- break;
- }
-
- return super.dispatchTouchEvent(motionEvent);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent motionEvent) {
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_MOVE:
-
- if (!dragLock) {
- int currentRawX = (int) motionEvent.getRawX();
- int currentRawY = (int) motionEvent.getRawY();
-
- if (dragView == null) {
- createDragImageView(getChildAt(pointToPosition(lastRawX, lastRawY) - getFirstVisiblePosition()));
-
- getChildAt(originalPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
-
- if (dragingListener != null) {
- dragingListener.onStart(originalPosition);
- }
- }
- drag(currentRawY - lastRawY);
- if (dragingListener != null) {
- dragingListener.onDraging((int) motionEvent.getX(), (int) motionEvent.getY(), currentRawX, currentRawY);
- }
-
- int position = pointToPosition(dragCenterX, dragCenterY);
-
- // 满足交换条件时让目标位置的原有视图上滑或下滑
- if (position != AdapterView.INVALID_POSITION && currentPosition != position && adapter.isDragAvailable(position)) {
- translation(position, currentPosition);
- currentPosition = position;
-
- if (targetChangedListener != null) {
- targetChangedListener.onTargetChanged(currentPosition);
- }
- }
-
- // 更新点击位置
- lastX = (int) motionEvent.getX();
- lastY = (int) motionEvent.getY();
- lastRawX = currentRawX;
- lastRawY = currentRawY;
-
- // 返回true消耗掉这次点击事件,防止ListView本身接收到这次点击事件后触发滚动
- return true;
- }
- break;
- case MotionEvent.ACTION_UP:
- // 手指抬起时,如果所有滑动动画都已播放完毕,则直接执行拖动完成逻辑
- if (animatorList.size() == 0) {
- resetDataAndView();
- if (dragingListener != null) {
- dragingListener.onFinish(currentPosition);
- }
- }
- // 如果还有未播放完成的滑动动画,则注册观察者,延时执行拖动完成逻辑
- else {
- animatorObserver = new AnimatorObserver() {
- @Override
- public void onAllAnimatorFinish() {
- resetDataAndView();
- if (dragingListener != null) {
- dragingListener.onFinish(currentPosition);
- }
- }
- };
- }
- break;
- }
-
- return super.onTouchEvent(motionEvent);
- }
-
- /**
- * 创建拖动块视图方法
- *
- * @param view 被拖动位置的视图对象
- */
- private void createDragImageView(View view) {
-
- if (view == null) {
- return;
- }
-
- removeDragImageView();
- int[] location = new int[2];
-
- view.getLocationOnScreen(location);
- view.setDrawingCacheEnabled(true);
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
- view.destroyDrawingCache();
-
- windowLayoutParams = new WindowManager.LayoutParams();
- windowLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
- windowLayoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- windowLayoutParams.format = PixelFormat.TRANSPARENT;
- windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
- windowLayoutParams.x = location[0];
- windowLayoutParams.y = location[1] - statusHeight;
- windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-
- dragCenterX = windowLayoutParams.x + view.getWidth() / 2;
- dragCenterY = windowLayoutParams.y + view.getHeight() / 2;
-
- dragView = dragViewCreator.createDragView(view.getWidth(), view.getHeight(), bitmap);
-
- if (dragView == null) {
- throw new NullPointerException("dragView can not be null");
- } else {
- windowManager.addView(dragView, windowLayoutParams);
- }
- }
-
- /**
- * 移除拖动视图方法
- */
- private void removeDragImageView() {
- if (dragView != null && windowManager != null) {
- windowManager.removeView(dragView);
- dragView = null;
- windowLayoutParams = null;
- }
- }
-
- /**
- * 拖动方法
- *
- * @param dy
- */
- private void drag(int dy) {
- dragCenterY += dy;
- windowLayoutParams.y += dy;
- windowManager.updateViewLayout(dragView, windowLayoutParams);
-
- handler.post(scrollRunnable);
- }
-
- /**
- * 移动指定位置视图方法
- *
- * @param fromPosition 移动起始位置
- * @param toPosition 移动目标位置
- */
- private void translation(int fromPosition, int toPosition) {
-
- View fromView = getChildAt(fromPosition - getFirstVisiblePosition());
- View toView = getChildAt(toPosition - getFirstVisiblePosition());
-
- if (fromView == null || toView == null) {
- return;
- }
-
- float distance = (toView.getY() - toView.getTranslationY()) - (fromView.getY() - fromView.getTranslationY());
-
- ObjectAnimator animator = ObjectAnimator.ofFloat(fromView, "translationY", 0, distance);
- animator.setDuration(duration);
- animator.setInterpolator(getAnimatorInterpolator());
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animatorList.remove(animation);
- // 所有滑动动画都播放结束时,执行相关操作
- if (animatorList.size() == 0) {
- // 重置所有滑动过的视图的translateY,避免列表刷新后视图重叠
- resetTranslate(dragViews);
- dragViews.clear();
- adapter.exchangeData(originalPosition, currentPosition);
- addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (dragRefresh) {
- removeOnLayoutChangeListener(this);
- resetChildVisibility();
- getChildAt(currentPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
- originalPosition = currentPosition;
- dragRefresh = false;
-
- if (animatorObserver != null) {
- animatorObserver.onAllAnimatorFinish();
- animatorObserver = null;
- }
- }
- }
- });
- dragRefresh = true;
- adapter.notifyDataSetChanged();
- }
- }
- });
-
- animatorList.add(animator);
- dragViews.add(fromView);
- animator.start();
- }
-
- /**
- * 重置列表所有项的可见性方法
- */
- private void resetChildVisibility() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != null) {
- child.setVisibility(VISIBLE);
- }
- }
- }
-
- /**
- * 重置指定视图的translateY属性方法
- *
- * @param list
- */
- private void resetTranslate(ArrayList<View> list) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) != null) {
- list.get(i).setTranslationY(0);
- }
- }
- }
-
- /**
- * 重置数据和视图相关数据方法
- */
- private void resetDataAndView() {
- if (currentPosition == -1) {
- return;
- }
- getChildAt(currentPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);
- originalPosition = -1;
- currentPosition = -1;
- handler.removeCallbacks(scrollRunnable);
- removeDragImageView();
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
- if (adapter instanceof BaseDragAdapter) {
- this.adapter = (BaseDragAdapter) adapter;
- super.setAdapter(adapter);
- } else {
- throw new IllegalStateException("the adapter must extends BaseDragAdapter");
- }
- }
-
- /**
- * 根据速度模板创建动画迭代器
- *
- * @return
- */
- private Interpolator getAnimatorInterpolator() {
- switch (speedMode) {
- case MODE_LINEAR:
- return new LinearInterpolator();
- case MODE_ACCELERATE:
- return new AccelerateInterpolator();
- case MODE_DECELERATE:
- return new DecelerateInterpolator();
- case MODE_ACCELERATE_DECELERATE:
- return new AccelerateDecelerateInterpolator();
- default:
- return null;
- }
- }
-
- /**
- * 拖动解锁方法,调用者需手动调用该方法后才能开启列表拖动功能
- */
- public void unlockDrag() {
- dragLock = false;
- }
-
- /**
- * 拖动锁定方法,调用者调用该方法后关闭列表拖动功能
- */
- public void lockDrag() {
- dragLock = true;
- }
-
- /**
- * 设置移动动画持续时间
- *
- * @param duration 时间,单位毫秒
- */
- public void setDuration(long duration) {
- this.duration = duration;
- }
-
- /**
- * 设置速度模式,可选项:
- * MODE_LINEAR 线性变化模式
- * MODE_ACCELERATE 加速模式
- * MODE_DECELERATE 减速模式
- * MODE_ACCELERATE_DECELERATE 先加速后加速模式
- *
- * @param speedMode
- */
- public void setSpeedMode(int speedMode) {
- this.speedMode = speedMode;
- }
-
- /**
- * 设置自动滚动速度
- *
- * @param scrollSpeed 速度,单位:dp/10ms
- */
- public void setScrollSpeed(int scrollSpeed) {
- this.scrollSpeed = scrollSpeed;
- }
-
- /**
- * 设置拖动块视图对象生成器方法
- *
- * @param creator
- */
- public void setDragViewCreator(DragViewCreator creator) {
- if (creator == null) {
- return;
- }
-
- this.dragViewCreator = creator;
- }
-
- /**
- * 设置拖动监听接口
- *
- * @param dragingListener
- */
- public void setOnDragingListener(OnDragingListener dragingListener) {
- this.dragingListener = dragingListener;
- }
-
- /**
- * 设置拖动目标位置改变监听接口
- *
- * @param targetChangedListener
- */
- public void setOnDragTargetChangedListener(OnDragTargetChangedListener targetChangedListener) {
- this.targetChangedListener = targetChangedListener;
- }
-
- private int getStatusHeight() {
- int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resourceId > 0) {
- return context.getResources().getDimensionPixelSize(resourceId);
- }
-
- return 0;
- }
-
- /**
- * 动画观察者
- */
- private interface AnimatorObserver {
-
- /**
- * 滑动动画播放结束时回调
- */
- void onAllAnimatorFinish();
-
- }
-
- /**
- * 拖动块视图对象生成器
- */
- public interface DragViewCreator {
-
- /**
- * 创建拖动块视图对象方法,可通过实现该方法自定义拖动块样式
- */
- View createDragView(int width, int height, Bitmap viewCache);
-
- }
-
- /**
- * 拖动监听接口
- */
- public interface OnDragingListener {
-
- /**
- * 拖动开始时回调
- *
- * @param startPosition 拖动起始坐标
- */
- void onStart(int startPosition);
-
- /**
- * 拖动过程中回调
- *
- * @param x 触控点相对ListView的x坐标
- * @param y 触控点相对ListView的y坐标
- * @param rawX 触控点相对屏幕的x坐标
- * @param rawY 触控点相对屏幕的y坐标
- */
- void onDraging(int x, int y, int rawX, int rawY);
-
- /**
- * 拖动结束时回调
- *
- * @param finalPosition 拖动终点坐标
- */
- void onFinish(int finalPosition);
-
- }
-
- /**
- * 拖动目标位置改变监听接口
- */
- public interface OnDragTargetChangedListener {
-
- /**
- * 拖动过程中,每次目标位置改变,会在该方法回调
- *
- * @param targetPosition 拖动目标位置坐标
- */
- void onTargetChanged(int targetPosition);
-
- }
- }

简单讲一下实现原理。手指按下时通过ListView的getChildAt方法获得按下位置的item并获取其视图缓存,也就是这句话:
- view.setDrawingCacheEnabled(true);
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
- view.destroyDrawingCache();
然后新建一个View把这个缓存塞进去并置于屏幕之上,并隐藏原来的item,让人看起来就好像是item被“拽”了下来,也就是这句话:
windowManager.addView(dragView, windowLayoutParams);
手指移动时,改变这个View的LayoutParams的y坐标值,让它跟随手指移动,也就是这两句话:
- windowLayoutParams.y += dy;
- windowManager.updateViewLayout(dragView, windowLayoutParams);
拖拽过程中,当判定交换行为发生时,用一个属性动画不断改变目标item的translationY属性来实现交换效果,也就是这句话:
ObjectAnimator animator = ObjectAnimator.ofFloat(fromView, "translationY", 0, distance);
具体代码大家可以看注释,应该写得比较清楚了。
要特别说明的是,DragListView的setAdapter方法被重写了,只接收BaseDragAdapter的继承类,BaseDragAdapter长这样:
- public abstract class BaseDragAdapter extends BaseAdapter {
-
- /**
- * 调用者需实现该方法,返回列表的所有数据集合
- *
- * @return
- */
- public abstract List getDataList();
-
- /**
- * 调用者可实现该方法自定义某一项是否可被拖动
- *
- * @param position
- * @return
- */
- public abstract boolean isDragAvailable(int position);
-
- /**
- * 实现数据交换方法
- *
- * @param oldPosition
- * @param newPosition
- */
- public void exchangeData(int oldPosition, int newPosition) {
-
- List list = getDataList();
- if (list == null) {
- return;
- }
- Object temp = list.get(oldPosition);
- if (oldPosition < newPosition) {
- for (int i = oldPosition; i < newPosition; i++) {
- Collections.swap(list, i, i + 1);
- }
- } else if (oldPosition > newPosition) {
- for (int i = oldPosition; i > newPosition; i--) {
- Collections.swap(list, i, i - 1);
- }
- }
- list.set(newPosition, temp);
- }
- }

BaseDragAdapter的目的是替调用者封装一些必要的操作,它给普通的BaseAdapter增加了两个需要实现的抽象方法:getDataList()和isDragAvailable()。getDataList()返回ListView 的数据列表即可,isDragAvailable()用来让调用者决定某个item是否可被拖拽,比如说需求是列表的第一项不可被拖拽,只需要实现isDragAvailable方法,在position=0时返回false即可。
然后就可以使用了。先写一个item的布局:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#FFFAFA"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/content_textview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="15dp"
- android:paddingTop="20dp"
- android:paddingRight="15dp"
- android:paddingBottom="20dp"
- android:text="我是内容"
- android:textSize="20sp" />
-
- <View
- android:id="@+id/divider_line"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginLeft="15dp"
- android:background="#eeeeee" />
-
- </LinearLayout>

再简单写一个适配器TestListViewAdapter继承自BaseDragAdapter:
- public class TestListViewAdapter extends BaseDragAdapter {
-
- private Context context;
- private int resourceId;
- private ArrayList<String> list;
-
- private Vibrator vibrator;
-
- private OnItemLongClickListener listener;
-
- public TestListViewAdapter(Context context, int resourceId, ArrayList<String> list) {
- this.context = context;
- this.resourceId = resourceId;
- this.list = list;
- this.vibrator = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
- }
-
- @Override
- public List getDataList() {
- return list;
- }
-
- @Override
- public boolean isDragAvailable(int position) {
- return true;
- }
-
- @Override
- public int getCount() {
- return list.size();
- }
-
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- View view;
- ViewHolder viewHolder;
- if (convertView == null) {
- view = LayoutInflater.from(context).inflate(resourceId, null);
- viewHolder = new ViewHolder();
- viewHolder.itemLayout = view.findViewById(R.id.item_layout);
- viewHolder.contentTextView = view.findViewById(R.id.content_textview);
- viewHolder.dividerLine = view.findViewById(R.id.divider_line);
- view.setTag(viewHolder);
- } else {
- view = convertView;
- viewHolder = (ViewHolder) view.getTag();
- }
-
- viewHolder.contentTextView.setText(list.get(position));
-
- viewHolder.dividerLine.setVisibility(position != list.size() - 1 ? View.VISIBLE : View.INVISIBLE);
-
- viewHolder.itemLayout.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- vibrator.vibrate(100);
-
- if (listener != null) {
- listener.onItemLongClick(position);
- }
-
- return false;
- }
- });
-
- return view;
- }
-
- public void setOnItemLongClickListener(OnItemLongClickListener listener) {
- this.listener = listener;
- }
-
- public interface OnItemLongClickListener {
-
- void onItemLongClick(int position);
-
- }
-
- class ViewHolder {
- LinearLayout itemLayout;
- TextView contentTextView;
- View dividerLine;
- }
- }

代码很简单,就不多说了。
最后就可以使用了,Activity里这样写:
- private DragListView dragListView;
-
- private TestListViewAdapter adapter;
-
- private ArrayList<String> list;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_drag_listview_test);
-
- initData();
- initView();
- }
-
- private void initData() {
- list = new ArrayList<>();
-
- for (int i = 1; i <= 40; i++) {
- list.add("我是第" + i + "条数据");
- }
- }
-
- private void initView() {
- dragListView = findViewById(R.id.drag_listview);
-
- dragListView.setOnDragingListener(new DragListView.OnDragingListener() {
- @Override
- public void onStart(int startPosition) {
-
- }
-
- @Override
- public void onDraging(int x, int y, int rawX, int rawY) {
-
- }
-
- @Override
- public void onFinish(int finalPosition) {
- dragListView.lockDrag();
- }
- });
-
- adapter = new TestListViewAdapter(this, R.layout.item_test_listview, list);
-
- adapter.setOnItemLongClickListener(new TestListViewAdapter.OnItemLongClickListener() {
- @Override
- public void onItemLongClick(int position) {
- dragListView.unlockDrag();
- }
- });
-
- dragListView.setAdapter(adapter);
- }

用法和普通的ListView一样,通过调用unlockDrag()来解锁拖动(示例代码中通过长按操作来解锁),通过调用lockDrag()方法来锁定拖动。之后还可以通过设置OnDragingListener来监听拖拽过程。开启和锁定拖动操作的条件视项目需求而定,比如长安开启,或者按编辑按钮开启等等。
最后运行一下就可以看到开头的效果了。
控件支持自定义拖拽View的样式。可以通过setDragViewCreator()方法来实现。比如说我想给拖拽的View加一个高亮效果,就可以这样写:
- dragListView.setDragViewCreator(new DragListView.DragViewCreator() {
- @Override
- public View createDragView(int width, int height, Bitmap viewCache) {
- RelativeLayout layout = new RelativeLayout(DragListViewTestActivity.this);
-
- ImageView imageView = new ImageView(DragListViewTestActivity.this);
- imageView.setImageBitmap(viewCache);
- layout.addView(imageView, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height));
-
- View view = new View(DragListViewTestActivity.this);
- view.setBackground(getDrawable(R.drawable.edging_red));
- layout.addView(view, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height));
-
- return layout;
- }
- });

其中高亮的资源edging_red.xml长这样:
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
-
- <stroke
- android:width="1dp"
- android:color="#FF6347" />
-
- <solid android:color="#70FFC0CB" />
-
- </shape>
代码很简单,就是新建一个Layout,里面放一张图片,再在之上加一层高亮遮罩,并将这个layout返回给DragViewCreator接口即可。运行一下看一下效果:
同样的原理再写一个支持item拖拽的GridView,上源码:
- public class DragGridView extends GridView {
-
- /**
- * 速度模板,影响视图移动时的速度变化
- * <p>
- * MODE_LINEAR // 线性变化模式
- * MODE_ACCELERATE // 加速模式
- * MODE_DECELERATE // 减速模式
- * MODE_ACCELERATE_DECELERATE // 先加速后加速模式
- */
- public static final int MODE_LINEAR = 0x001;
- public static final int MODE_ACCELERATE = 0x002;
- public static final int MODE_DECELERATE = 0x003;
- public static final int MODE_ACCELERATE_DECELERATE = 0x004;
-
- private Context context;
-
- // 拖动时的视图
- private View dragView;
-
-
- private WindowManager windowManager;
- private WindowManager.LayoutParams windowLayoutParams;
- private BaseDragAdapter adapter;
-
- /**
- * 可设置选项
- */
- // 移动动画储持续时间,单位毫秒
- private long duration = 300;
-
- // 速度模板
- private int speedMode = MODE_ACCELERATE_DECELERATE;
-
- // 自动滚动的速度
- private int scrollSpeed = 50;
-
-
- /**
- * 运行参数
- */
- // 拖动块的原始坐标
- private int originalPosition = -1;
-
- // 拖动块当前所在坐标
- private int currentPosition = -1;
-
- // 用于记录上次点击事件MotionEvent.getX();
- private int lastX;
-
- // 用于记录上次点击事件MotionEvent.getY();
- private int lastY;
-
- // 用于记录上次点击事件MotionEvent.getRawX();
- private int lastRawX;
-
- // 用于记录上次点击事件MotionEvent.getRawY();
- private int lastRawY;
-
- // 拖动块中心点x坐标,用于判断拖动块所处的列表位置
- private int dragCenterX;
-
- // 拖动块中心点y坐标,用于判断拖动块所处的列表位置
- private int dragCenterY;
-
- // 滑动上边界,拖动块中心超过该边界时列表自动向下滑动
- private int upScrollBorder;
-
- // 滑动下边界,拖动块中心超过该边界时列表自动向上滑动
- private int downScrollBorder;
-
- // 状态栏高度
- private int statusHeight;
-
- // 拖动时的列表刷新标识符
- private boolean dragRefresh;
-
- // 拖动锁定标记,为false时选中块可被拖动
- private boolean dragLock = true;
-
- // 动画列表,存放当前屏幕上正在播放的所有滑动动画的动画对象
- private ArrayList<Animator> animatorList;
-
- // 视图列表,存放当前屏幕上正在播放的所有滑动动画的视图对象
- private ArrayList<View> dragViews;
-
- /**
- * 可监听接口
- */
- // 拖动块视图对象生成器,可通过设置该接口自定义一个拖动视图的样式,不设置时会有默认实现
- private DragViewCreator dragViewCreator;
-
- // 拖动监听接口,拖动开始和结束时会在该接口回调
- private OnDragingListener dragingListener;
-
- // 当前拖动目标位置改变时,每次改变都会在该接口回调
- private OnDragTargetChangedListener targetChangedListener;
-
-
- // 内部接口,动画观察者,滑动动画结束是回调
- private AnimatorObserver animatorObserver;
-
- private Handler handler = new Handler();
-
- // 列表自动滚动线程
- private Runnable scrollRunnable = new Runnable() {
- @Override
- public void run() {
- int scrollY;
-
- // 滚动到顶或到底时停止滚动
- if (getFirstVisiblePosition() == 0 || getLastVisiblePosition() == getCount() - 1) {
- handler.removeCallbacks(scrollRunnable);
- }
-
- // 触控点y坐标超过上边界时,出发列表自动向下滚动
- if (lastY > upScrollBorder) {
- scrollY = scrollSpeed;
- handler.postDelayed(scrollRunnable, 25);
- }
- // 触控点y坐标超过下边界时,出发列表自动向上滚动
- else if (lastY < downScrollBorder) {
- scrollY = -scrollSpeed;
- handler.postDelayed(scrollRunnable, 25);
- }
- // // 触控点y坐标处于上下边界之间时,停止滚动
- else {
- scrollY = 0;
- handler.removeCallbacks(scrollRunnable);
- }
-
- smoothScrollBy(scrollY, 10);
- }
- };
-
- public DragGridView(Context context) {
- super(context);
- init(context);
- }
-
- public DragGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
-
- /**
- * 初始化方法
- *
- * @param context
- */
- private void init(Context context) {
- this.context = context;
- statusHeight = getStatusHeight();
- windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- animatorList = new ArrayList<>();
- dragViews = new ArrayList<>();
-
- // 拖动块视图对象生成器的默认实现,返回一个与被拖动项外观一致的ImageView
- dragViewCreator = new DragGridView.DragViewCreator() {
- @Override
- public View createDragView(int width, int height, Bitmap viewCache) {
- ImageView imageView = new ImageView(DragGridView.this.context);
- imageView.setImageBitmap(viewCache);
-
- return imageView;
- }
- };
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent motionEvent) {
-
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN:
-
- downScrollBorder = getHeight() / 5;
- upScrollBorder = getHeight() * 4 / 5;
-
- // 手指按下时记录相关坐标
- lastX = (int) motionEvent.getX();
- lastY = (int) motionEvent.getY();
- lastRawX = (int) motionEvent.getRawX();
- lastRawY = (int) motionEvent.getRawY();
-
- currentPosition = pointToPosition(lastRawX, lastRawY);
-
- if (currentPosition == AdapterView.INVALID_POSITION || !adapter.isDragAvailable(currentPosition)) {
- return true;
- }
-
- originalPosition = currentPosition;
-
- break;
- }
-
- return super.dispatchTouchEvent(motionEvent);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent motionEvent) {
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_MOVE:
- if (!dragLock) {
- int currentRawX = (int) motionEvent.getRawX();
- int currentRawY = (int) motionEvent.getRawY();
-
- if (dragView == null) {
- createDragImageView(getChildAt(pointToPosition(lastRawX, lastRawY) - getFirstVisiblePosition()));
-
- getChildAt(originalPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
-
- if (dragingListener != null) {
- dragingListener.onStart(originalPosition);
- }
- }
- drag(currentRawX - lastRawX, currentRawY - lastRawY);
- if (dragingListener != null) {
- dragingListener.onDraging((int) motionEvent.getX(), (int) motionEvent.getY(), currentRawX, currentRawY);
- }
-
- int position = pointToPosition(dragCenterX, dragCenterY);
-
- if (position != AdapterView.INVALID_POSITION
- && currentPosition != position
- && adapter.isDragAvailable(position)
- && animatorList.size() == 0) {
-
- translation(position, currentPosition);
-
- currentPosition = position;
-
- if (targetChangedListener != null) {
- targetChangedListener.onTargetChanged(currentPosition);
- }
- }
-
- // 更新点击位置
- lastX = (int) motionEvent.getX();
- lastY = (int) motionEvent.getY();
- lastRawX = currentRawX;
- lastRawY = currentRawY;
-
- // 返回true消耗掉这次点击事件,防止ListView本身接收到这次点击事件后触发滚动
- return true;
- }
- break;
- case MotionEvent.ACTION_UP:
- // 手指抬起时,如果所有滑动动画都已播放完毕,则直接执行拖动完成逻辑
- if (animatorList.size() == 0) {
- resetDataAndView();
- if (dragingListener != null) {
- dragingListener.onFinish(currentPosition);
- }
- }
- // 如果还有未播放完成的滑动动画,则注册观察者,延时执行拖动完成逻辑
- else {
- animatorObserver = new AnimatorObserver() {
- @Override
- public void onAllAnimatorFinish() {
- resetDataAndView();
- if (dragingListener != null) {
- dragingListener.onFinish(currentPosition);
- }
- }
- };
- }
- break;
- }
-
- return super.onTouchEvent(motionEvent);
- }
-
- /**
- * 创建拖动块视图方法
- *
- * @param view 被拖动位置的视图对象
- */
- private void createDragImageView(View view) {
-
- if (view == null) {
- return;
- }
-
- removeDragImageView();
- int[] location = new int[2];
-
- view.getLocationOnScreen(location);
- view.setDrawingCacheEnabled(true);
- Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
- view.destroyDrawingCache();
-
- windowLayoutParams = new WindowManager.LayoutParams();
- windowLayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
- windowLayoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- windowLayoutParams.format = PixelFormat.TRANSPARENT;
- windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
- windowLayoutParams.x = location[0];
- windowLayoutParams.y = location[1] - statusHeight;
- windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-
- dragCenterX = windowLayoutParams.x + view.getWidth() / 2;
- dragCenterY = windowLayoutParams.y + view.getHeight() / 2;
-
- dragView = dragViewCreator.createDragView(view.getWidth(), view.getHeight(), bitmap);
-
- if (dragView == null) {
- throw new NullPointerException("dragView can not be null");
- } else {
- windowManager.addView(dragView, windowLayoutParams);
- }
- }
-
- /**
- * 移除拖动视图方法
- */
- private void removeDragImageView() {
- if (dragView != null && windowManager != null) {
- windowManager.removeView(dragView);
- dragView = null;
- windowLayoutParams = null;
- }
- }
-
- /**
- * 拖动方法
- *
- * @param dx
- * @param dy
- */
- private void drag(int dx, int dy) {
-
- dragCenterX += dx;
- dragCenterY += dy;
- windowLayoutParams.x += dx;
- windowLayoutParams.y += dy;
- windowManager.updateViewLayout(dragView, windowLayoutParams);
-
- handler.post(scrollRunnable);
- }
-
- /**
- * 移动指定位置视图方法
- *
- * @param fromPosition 移动起始位置
- * @param toPosition 移动目标位置
- */
- private void translation(int fromPosition, int toPosition) {
- ArrayList<Animator> list = new ArrayList<>();
- if (toPosition > fromPosition) {
- for (int position = fromPosition; position < toPosition; position++) {
- View view = getChildAt(position - getFirstVisiblePosition());
- dragViews.add(view);
- if ((position + 1) % getNumColumns() == 0) {
- list.add(createTranslationAnimations(view,
- 0,
- -(view.getWidth() + getVerticalSpacing()) * (getNumColumns() - 1),
- 0,
- view.getHeight() + getHorizontalSpacing()));
- } else {
- list.add(createTranslationAnimations(view,
- 0,
- view.getWidth() + getVerticalSpacing(),
- 0,
- 0));
- }
- }
- } else {
- for (int position = fromPosition; position > toPosition; position--) {
- View view = getChildAt(position - getFirstVisiblePosition());
- dragViews.add(view);
- if (position % getNumColumns() == 0) {
- list.add(createTranslationAnimations(view,
- 0,
- (view.getWidth() + getVerticalSpacing()) * (getNumColumns() - 1),
- 0,
- -(view.getHeight() + getHorizontalSpacing())));
- } else {
- list.add(createTranslationAnimations(view,
- 0,
- -(view.getWidth() + getVerticalSpacing()),
- 0,
- 0));
- }
- }
- }
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(list);
- animatorSet.setDuration(duration);
- animatorSet.setInterpolator(getAnimatorInterpolator());
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animatorList.remove(animation);
- // 所有滑动动画都播放结束时,执行相关操作
- if (animatorList.size() == 0) {
- // 重置所有滑动过的视图的translateY,避免列表刷新后视图重叠
- resetTranslate(dragViews);
- dragViews.clear();
- adapter.exchangeData(originalPosition, currentPosition);
- addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (dragRefresh) {
- removeOnLayoutChangeListener(this);
- resetChildVisibility();
- getChildAt(currentPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);
- originalPosition = currentPosition;
- dragRefresh = false;
-
- if (animatorObserver != null) {
- animatorObserver.onAllAnimatorFinish();
- animatorObserver = null;
- }
- }
- }
- });
- dragRefresh = true;
- adapter.notifyDataSetChanged();
- }
- }
- });
-
- animatorList.add(animatorSet);
- animatorSet.start();
- }
-
- /**
- * 生成移动动画方法
- *
- * @param view 需要移动的视图
- * @param startX 移动起始x坐标
- * @param endX 移动终点x坐标
- * @param startY 移动起始y坐标
- * @param endY 移动终点y坐标
- * @return
- */
- private Animator createTranslationAnimations(View view, float startX, float endX, float startY, float endY) {
- ObjectAnimator animatorX = ObjectAnimator.ofFloat(view, "translationX", startX, endX);
- ObjectAnimator animatorY = ObjectAnimator.ofFloat(view, "translationY", startY, endY);
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(animatorX, animatorY);
- return animatorSet;
- }
-
- /**
- * 重置列表所有项的可见性方法
- */
- private void resetChildVisibility() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != null) {
- child.setVisibility(VISIBLE);
- }
- }
- }
-
- /**
- * 重置指定视图的translateY属性方法
- *
- * @param list
- */
- private void resetTranslate(ArrayList<View> list) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) != null) {
- list.get(i).setTranslationX(0);
- list.get(i).setTranslationY(0);
- }
- }
- }
-
- /**
- * 重置数据和视图相关数据方法
- */
- private void resetDataAndView() {
- if (currentPosition == -1) {
- return;
- }
- getChildAt(currentPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);
- originalPosition = -1;
- currentPosition = -1;
- dragLock = true;
- handler.removeCallbacks(scrollRunnable);
- removeDragImageView();
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
- if (adapter instanceof BaseDragAdapter) {
- this.adapter = (BaseDragAdapter) adapter;
- super.setAdapter(adapter);
- } else {
- throw new IllegalStateException("the adapter must extends BaseDragAdapter");
- }
- }
-
- /**
- * 根据速度模板创建动画迭代器
- *
- * @return
- */
- private Interpolator getAnimatorInterpolator() {
- switch (speedMode) {
- case MODE_LINEAR:
- return new LinearInterpolator();
- case MODE_ACCELERATE:
- return new AccelerateInterpolator();
- case MODE_DECELERATE:
- return new DecelerateInterpolator();
- case MODE_ACCELERATE_DECELERATE:
- return new AccelerateDecelerateInterpolator();
- default:
- return null;
- }
- }
-
- /**
- * 拖动解锁方法,调用者需手动调用该方法后才能开启列表拖动功能
- */
- public void unlockDrag() {
- dragLock = false;
- }
-
- /**
- * 拖动锁定方法,调用者调用该方法后关闭列表拖动功能
- */
- public void lockDrag() {
- dragLock = true;
- }
-
- /**
- * 设置移动动画持续时间
- *
- * @param duration 时间,单位毫秒
- */
- public void setDuration(long duration) {
- this.duration = duration;
- }
-
- /**
- * 设置速度模式,可选项:
- * MODE_LINEAR 线性变化模式
- * MODE_ACCELERATE 加速模式
- * MODE_DECELERATE 减速模式
- * MODE_ACCELERATE_DECELERATE 先加速后加速模式
- *
- * @param speedMode
- */
- public void setSpeedMode(int speedMode) {
- this.speedMode = speedMode;
- }
-
- /**
- * 设置自动滚动速度
- *
- * @param scrollSpeed 速度,单位:dp/10ms
- */
- public void setScrollSpeed(int scrollSpeed) {
- this.scrollSpeed = scrollSpeed;
- }
-
- /**
- * 设置拖动块视图对象生成器方法
- *
- * @param creator
- */
- public void setDragViewCreator(DragViewCreator creator) {
- if (creator == null) {
- return;
- }
-
- this.dragViewCreator = creator;
- }
-
- /**
- * 设置拖动监听接口
- *
- * @param dragingListener
- */
- public void setOnDragingListener(OnDragingListener dragingListener) {
- this.dragingListener = dragingListener;
- }
-
- /**
- * 设置拖动目标位置改变监听接口
- *
- * @param targetChangedListener
- */
- public void setOnDragTargetChangedListener(OnDragTargetChangedListener targetChangedListener) {
- this.targetChangedListener = targetChangedListener;
- }
-
- private int getStatusHeight() {
- int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resourceId > 0) {
- return context.getResources().getDimensionPixelSize(resourceId);
- }
-
- return 0;
- }
-
- /**
- * 动画观察者
- */
- private interface AnimatorObserver {
-
- /**
- * 滑动动画播放结束时回调
- */
- void onAllAnimatorFinish();
-
- }
-
- /**
- * 拖动块视图对象生成器
- */
- public interface DragViewCreator {
-
- /**
- * 创建拖动块视图对象方法,可通过实现该方法自定义拖动块样式
- */
- View createDragView(int width, int height, Bitmap viewCache);
-
- }
-
- /**
- * 拖动监听接口
- */
- public interface OnDragingListener {
-
- /**
- * 拖动开始时回调
- *
- * @param startPosition 拖动起始坐标
- */
- void onStart(int startPosition);
-
- /**
- * 拖动过程中回调
- *
- * @param x 触控点相对ListView的x坐标
- * @param y 触控点相对ListView的y坐标
- * @param rawX 触控点相对屏幕的x坐标
- * @param rawY 触控点相对屏幕的y坐标
- */
- void onDraging(int x, int y, int rawX, int rawY);
-
- /**
- * 拖动结束时回调
- *
- * @param finalPosition 拖动终点坐标
- */
- void onFinish(int finalPosition);
-
- }
-
- /**
- * 拖动目标位置改变监听接口
- */
- public interface OnDragTargetChangedListener {
-
- /**
- * 拖动过程中,每次目标位置改变,会在该方法回调
- *
- * @param targetPosition 拖动目标位置坐标
- */
- void onTargetChanged(int targetPosition);
-
- }
- }

实现原理和DragListView差不多,就不多做解释了。DragGridView的setAdapter方法同样只接收BaseDragAdapter的继承类,用法和DragListView一样。
简单使用一下,先写一个item布局item_test_gridview.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item_layout"
- android:layout_width="match_parent"
- android:minHeight="130dp"
- android:layout_height="match_parent"
- android:background="#FFFAFA"
- android:gravity="center_horizontal"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/content_textview"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:gravity="center"
- android:padding="15dp"
- android:text="我是内容"
- android:textSize="20sp" />
-
- </LinearLayout>

再写一个适配器TestGridViewAdapter:
- public class TestGridViewAdapter extends BaseDragAdapter {
-
- private Context context;
- private int resourceId;
- private ArrayList<String> list;
-
- private Vibrator vibrator;
-
- private OnItemLongClickListener listener;
-
- public TestGridViewAdapter(Context context, int resourceId, ArrayList<String> list) {
- this.context = context;
- this.resourceId = resourceId;
- this.list = list;
- this.vibrator = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
- }
-
- @Override
- public List getDataList() {
- return list;
- }
-
- @Override
- public boolean isDragAvailable(int position) {
- return true;
- }
-
- @Override
- public int getCount() {
- return list.size();
- }
-
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- View view;
- ViewHolder viewHolder;
- if (convertView == null) {
- view = LayoutInflater.from(context).inflate(resourceId, null);
- viewHolder = new ViewHolder();
- viewHolder.itemLayout = view.findViewById(R.id.item_layout);
- viewHolder.contentTextView = view.findViewById(R.id.content_textview);
- view.setTag(viewHolder);
- } else {
- view = convertView;
- viewHolder = (ViewHolder) view.getTag();
- }
-
- viewHolder.contentTextView.setText(list.get(position));
-
- viewHolder.itemLayout.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- vibrator.vibrate(100);
-
- if (listener != null) {
- listener.onItemLongClick(position);
- }
-
- return false;
- }
- });
-
- return view;
- }
-
- public void setOnItemLongClickListener(OnItemLongClickListener listener) {
- this.listener = listener;
- }
-
- public interface OnItemLongClickListener {
-
- void onItemLongClick(int position);
-
- }
-
- class ViewHolder {
- LinearLayout itemLayout;
- TextView contentTextView;
- }
- }

最后Activity这样写:
- private DragGridView dragGridView;
-
- private TestGridViewAdapter adapter;
-
- private ArrayList<String> list;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_drag_gridview_test);
-
- initData();
- initView();
- }
-
- private void initData() {
- list = new ArrayList<>();
-
- for (int i = 1; i <= 40; i++) {
- list.add("我是第" + i + "条数据");
- }
- }
-
- private void initView() {
- dragGridView = findViewById(R.id.drag_gridview);
-
- dragGridView.setOnDragingListener(new DragGridView.OnDragingListener() {
- @Override
- public void onStart(int startPosition) {
-
- }
-
- @Override
- public void onDraging(int x, int y, int rawX, int rawY) {
-
- }
-
- @Override
- public void onFinish(int finalPosition) {
- dragGridView.lockDrag();
- }
- });
-
- adapter = new TestGridViewAdapter(this, R.layout.item_test_gridview, list);
-
- adapter.setOnItemLongClickListener(new TestGridViewAdapter.OnItemLongClickListener() {
- @Override
- public void onItemLongClick(int position) {
- dragGridView.unlockDrag();
- }
- });
-
- dragGridView.setAdapter(adapter);
- }

用法和DragListView一毛一样。运行一下就能看到开头的效果了。
DragGridView同样可以自定义拖拽View的样式,同样通过setDragViewCreator()方法来实现。比如说添加一个高亮效果:
- dragGridView.setDragViewCreator(new DragGridView.DragViewCreator() {
- @Override
- public View createDragView(int width, int height, Bitmap viewCache) {
- RelativeLayout layout = new RelativeLayout(DragGridViewTestActivity.this);
-
- ImageView imageView = new ImageView(DragGridViewTestActivity.this);
- imageView.setImageBitmap(viewCache);
- layout.addView(imageView, new RelativeLayout.LayoutParams(width, height));
-
- View view = new View(DragGridViewTestActivity.this);
- view.setBackground(getDrawable(R.drawable.edging_red));
- layout.addView(view, new RelativeLayout.LayoutParams(width, height));
-
- return layout;
- }
- });

看看效果:
以上就是全部内容了,最后来总结一下。
DragListView和DragGridView分别实现ListView和GridView的item拖拽功能。接收Adapter必须是BaseDragAdapter的继承类,通过unlockDrag()方法和lockDrag()方法来开启和关闭拖动。提供OnDragingListener接口来监听拖动过程,提供DragViewCreator接口来自定义拖拽样式。
最后的最后,附上源码地址:https://download.csdn.net/download/Sure_Min/12572918
这次的内容就到这里,我们下次再见。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。