可以拖动交换item位置的GridView

欢迎关注Android技术分享公众号(小红人)。

http://blog.csdn.net/kymjs/article/details/43817733

这篇文章是基于夏安明写的一个可以移动item的Demo改写的,因为原代码有一些BUG,比如adapter不能使用ViewHolder优化(这个问题应该是最大的问题)再比如不能使用上下拉刷新功能(这个是我额外添加的功能,不知道的可以去看看开源中国手机客户端的便签功能)

在Android开发中,我们常常用到ListView和GridView,而有的时候系统的ListView,GridView并不能满足我们的需求,所以我们需要自己定义一个ListView或者GridView,今天这篇文章就给大家来自定义GridView的控件,GridView主要是来显示网格的控件,在Android的开发中使用很普通,相对于TextView,Button这些控件来说要来的复杂些,今天给大家带来长按GridView的item,然后将其拖拽其他item上面,使得GridView的item发生交换,比较典型的就是我们的Launcher,网上有很多关于GridView的拖动的Demo,但是大部分都是相同的,而且存在一些Bug,而且大部分都是点击GridView的item然后进行拖动,或者item之间不进行实时交换,今天给大家更加详细的介绍GridView拖拽,并且将Demo做的更完美,大家更容易接受,也许很多人听到这个感觉实现起来很复杂,就关掉的这篇文章,其实告诉大家,只要知道了思路就感觉一点都不复杂了,不信大家可以接着往下看看,首先还是跟大家说说实现的思路

  1. 根据手指按下的X,Y坐标来获取我们在GridView上面点击的item

  2. 手指按下的时候使用Handler和Runnable来实现一个定时器,假如定时时间为1000毫秒,在1000毫秒内,如果手指抬起了移除定时器,没有抬起并且手指点击在GridView的item所在的区域,则表示我们长按了GridView的item

  3. 如果我们长按了item则隐藏item,然后使用WindowManager来添加一个item的镜像在屏幕用来代替刚刚隐藏的item

  4. 当我们手指在屏幕移动的时候,更新item镜像的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置

  5. 到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动

  6. GridView交换数据,刷新界面,移除item的镜像

看完上面的这些思路你是不是找到了些感觉了呢?下面为大家讲讲核心部分——点击事件处理。

Android事件分发对于自定义控件很重要,简单说下,当我们点击GridView的Item,先会去执行dispatchTouchEvent()方法将事件分发下去,所以我们要重写dispatchTouchEvent()方法在手指按下的时候根据pointToPosition()方法来获取我们按下的item的position,根据getChildAt()方法来获取该position上面所对应的View, 并且开启长按的定时器,默认时间为1000毫秒,如果在1000毫秒内手指抬起或者手指在屏幕上滑动出了该item,则取消长按定时器,否则就表示可以进行拖拽,手机友好的震动一下,隐藏我们长按的Item,屏幕调用createDragImage()方法来创建我们长按的item的镜像,创建Item的镜像使用的是WindowManager类,该类可以创建一个窗体显示在Activity之上。


再此之前大家先要理解这几个距离,理解这几个距离之前要首先知道getRawX(),getRawY()和getX(),getY()的区别,getRawX(),getRawY()是相对于屏幕的原点的距离,而getX(),getY()是相对于控件左上方的点的距离,为了方便大家理解我用Word简单的画了下图,画得不好,大家将就的看下,红色框框为我们的GridView




  • mPoint2ItemTop 手指按下的点到该Item的上边缘的距离,如上图的1号线

  • mPoint2ItemLeft 手指按下的点到该Item的左边缘的距离,如上图的2号线

  • mOffset2Top  GridView的上边缘到屏幕上边缘的距离,如上图的3号线,这个距离包裹状态栏,标题栏,或者一些在GridView上面的布局的高度,这个很重要我们现实Item镜像需要用到

  • mOffset2Left GridView的左边缘到屏幕左边缘的距离,如上图的4号线,我这个Demo的这个距离为0,因为我设置DragGridView的宽度为充满屏幕,但是我们要考虑假如GridView与屏幕左边缘设置了间隙或者左边有其他的布局的情形

  • mDownScrollBorder 这个距离表示当GridView的item过多的时候,手机一屏显示不完全,我们拖动Item镜像到这个高度的时候,GridView自动向下滚动,如上图的5号线

  • .mUpScrollBorder 这个和mDownScrollBorder相反,当我们大于这个高度的时候,GridView自动向上滚动,如上图的6号线

理解了这六个距离,我们就来看看创建Item镜像的方法里面,其他的我不多说,首先设置format为PixelFormat.TRANSLUCENT,表示除了我们显示图片和文字的其他地方为透明,之后就是x,y这两个距离的计算,计算的是item的左上角的坐标,理解了上面这六个距离我们很容易得出x,y的坐标,可是你会发现y的坐标减去了状态栏的高度,这点大家需要注意下,另外我们需要获取item的绘制缓存的Bitmap对象,然后将Bitmap设置到一个ImageView上面,为什么要这么做呢?如果调用addView()方法将item 直接添加到WindowManager里面,会有异常产生,因为item已经有了自己归属的父容器DragGridView,所有我们这里使用一个ImageView来代替item添加到WindowManager里面

上面已经完成了开始拖拽的准备工作,要想拖动镜像我们还需要重写onTouchEvent()方法,获取移动的X,Y的坐标,利用WindowManager的updateViewLayout方法就能对镜像进行拖动,拖动的镜像的时候为了有更好的用户体验,我们还要做item的实时交换效果,我们利用手指移动的X,Y坐标,利用pointToPosition()来获取拖拽到的position,然后将之前的item显示出来,将拖拽到的item进行隐藏,这样子就完成了item在界面上面的交换,但是数据交换我这里没有做,所以我提供了回调接口OnChanageListener,我们只需要自己实现数据的交换逻辑然后刷新DragGridView即可,我们还需要实现DragGridView的自动向上滚动或者向下滚动,使用Handler和mScrollRunnable利用smoothScrollToPositionFromTop()来实现DragGridView滚动,具体的实现大家可以看代码
手指离开界面,将item的镜像移除,并将拖拽到的item显示出来,这样子就实现了GirdView的拖拽效果啦。





好了,今天的讲解就到此结束,效果还不错吧,看完这篇文章你是不是觉得GridView拖拽也不是那么难实现呢?你心里是不是也大概有自己的一个思路,建议大家自己敲敲看看,可以自己去实现下ListView的拖拽实现,ListView比GridView简单些。关于本文的代码,需要进一步理解的话,请加QQ群201055521群共享中查看,我已经贴出了所有的Java代码,注释也很详尽,可以方便理解,实在不懂,还可以回复本公众号或加我的个人微信766136833询问。

最后我们来看看自定义控件的完整代码

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 感谢这篇博客的作者,http://blog.csdn.net/xiaanming/article/details/17718579<br> 
  3.  * 在这个基础上解决了原作者的问题:Adapter无法使用ViewHolder优化的问题,优化了手势识别率,并添加了trashView功能, 
  4.  * 优化自定义控件对外扩展性,解决在上下拉环境下手势冲突问题<br> 
  5.  *  
  6.  * @author kymjs (https://github.com/kymjs) 
  7.  */  
  8. public class KJDragGridView extends GridView {  
  9.   
  10.     private long dragResponseMS = 700// item长按响应的时间  
  11.     private int mDragPosition;// 正在拖拽的position  
  12.   
  13.     private boolean isDrag = false// 是否可以拖拽,用于控件内部逻辑实现  
  14.     private boolean canDrag = true// 是否可用拖拽,主要用于外部开放设置  
  15.     private boolean mAnimationEnd = true;  
  16.   
  17.     private int mDownX;  
  18.     private int mDownY;  
  19.     private int moveX;  
  20.     private int moveY;  
  21.   
  22.     private View mStartDragItemView = null// 刚开始拖拽的item对应的View  
  23.     private ImageView mDragImageView; // 用于拖拽时显示的幻影镜像  
  24.     private Bitmap mDragBitmap; // 幻影镜像对应的Bitmap  
  25.     private View mTrashView; // 删除item的垃圾桶图标  
  26.   
  27.     private final Vibrator mVibrator; // 震动器  
  28.     private final int mStatusHeight;// 状态栏的高度  
  29.     private final WindowManager mWindowManager;  
  30.     private WindowManager.LayoutParams mWindowLayoutParams; // item镜像的布局参数  
  31.   
  32.     private int mPoint2ItemTop; // 按下的点到所在item的上边缘的距离  
  33.     private int mPoint2ItemLeft;  
  34.     private int mOffset2Top; // DragGridView距离屏幕顶部的偏移量  
  35.     private int mOffset2Left;  
  36.   
  37.     private int mDownScrollBorder; // DragGridView自动向下滚动的边界值  
  38.     private int mUpScrollBorder; // DragGridView自动向上滚动的边界值  
  39.   
  40.     private DragGridBaseAdapter mDragAdapter;  
  41.     private int mNumColumns;  
  42.     private int mColumnWidth;  
  43.     private boolean mNumColumnsSet;  
  44.     private int mHorizontalSpacing;  
  45.   
  46.     private static final int speed = 20// DragGridView自动滚动的速度  
  47.     private static final int MOVE_OFFSET = 25;  
  48.     private boolean moved = false;  
  49.   
  50.     public static final int HANDLE_START = 0x3587;  
  51.     public static final int HANDLE_CANCLE = 0x3588;  
  52.     public static final int HANDLE_FINISH = 0x3589;  
  53.     private static OnMoveListener moveListener; // 拖拽开始与结束监听器  
  54.     private OnDeleteListener deleteListener; // 移动到垃圾桶时的监听器  
  55.   
  56.     private final TouchRect moveRect = new TouchRect();  
  57.     private final TouchRect gridRect = new TouchRect();  
  58.     private final TouchRect trashRect = new TouchRect();  
  59.   
  60.     public KJDragGridView(Context context) {  
  61.         this(context, null);  
  62.     }  
  63.   
  64.     public KJDragGridView(Context context, AttributeSet attrs) {  
  65.         this(context, attrs, 0);  
  66.     }  
  67.   
  68.     public KJDragGridView(Context context, AttributeSet attrs, int defStyle) {  
  69.         super(context, attrs, defStyle);  
  70.         mVibrator = (Vibrator) context  
  71.                 .getSystemService(Context.VIBRATOR_SERVICE);  
  72.         mWindowManager = (WindowManager) context  
  73.                 .getSystemService(Context.WINDOW_SERVICE);  
  74.         mStatusHeight = getStatusHeight(context); // 获取状态栏的高度  
  75.   
  76.         if (!mNumColumnsSet) {  
  77.             mNumColumns = AUTO_FIT;  
  78.         }  
  79.     }  
  80.   
  81.     /** 
  82.      * 获取状态栏的高度 
  83.      *  
  84.      * @param context 
  85.      * @return 
  86.      */  
  87.     private static int getStatusHeight(Context context) {  
  88.         int statusHeight = 0;  
  89.         Rect localRect = new Rect();  
  90.   
  91.         ((Activity) context).getWindow().getDecorView()  
  92.                 .getWindowVisibleDisplayFrame(localRect);  
  93.         statusHeight = localRect.top;  
  94.         if (0 == statusHeight) {  
  95.             Class<?> localClass;  
  96.             try {  
  97.                 localClass = Class.forName("com.android.internal.R$dimen");  
  98.                 Object localObject = localClass.newInstance();  
  99.                 int i5 = Integer.parseInt(localClass  
  100.                         .getField("status_bar_height").get(localObject)  
  101.                         .toString());  
  102.                 statusHeight = context.getResources().getDimensionPixelSize(i5);  
  103.             } catch (Exception e) {  
  104.                 e.printStackTrace();  
  105.             }  
  106.         }  
  107.         return statusHeight;  
  108.     }  
  109.   
  110.     private static final Handler mHandler = new Handler() {  
  111.         @Override  
  112.         public void handleMessage(android.os.Message msg) {  
  113.             if (moveListener != null) {  
  114.                 if (msg.what == HANDLE_START) {  
  115.                     moveListener.startMove();  
  116.                 } else if (msg.what == HANDLE_FINISH) {  
  117.                     moveListener.finishMove();  
  118.                 } else if (msg.what == HANDLE_CANCLE) {  
  119.                     moveListener.cancleMove();  
  120.                 }  
  121.             }  
  122.         };  
  123.     };  
  124.   
  125.     // 用来处理是否为长按的Runnable  
  126.   
  127.     private final Runnable mLongClickRunnable = new Runnable() {  
  128.         @Override  
  129.         public void run() {  
  130.             if (!canDrag) {  
  131.                 return;  
  132.             }  
  133.             isDrag = true// 设置可以拖拽  
  134.             moved = true;  
  135.             mHandler.sendEmptyMessage(HANDLE_START);  
  136.             mVibrator.vibrate(50); // 震动一下  
  137.   
  138.             mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item  
  139.   
  140.             createDragImage(mDragBitmap, mDownX, mDownY);  
  141.             mDragBitmap = null;  
  142.         }  
  143.     };  
  144.   
  145.     /** 
  146.      * 若设置为AUTO_FIT,计算有多少列 
  147.      */  
  148.     @Override  
  149.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  150.         if (mNumColumns == AUTO_FIT) {  
  151.             int numFittedColumns = 1// 可用列  
  152.   
  153.             if (mColumnWidth > 0) {  
  154.                 int gridWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec)  
  155.                         - getPaddingLeft() - getPaddingRight(), 0);  
  156.                 numFittedColumns = gridWidth / mColumnWidth;  
  157.                 if (numFittedColumns > 0) {  
  158.                     while (numFittedColumns != 1) {  
  159.                         if (numFittedColumns * mColumnWidth  
  160.                                 + (numFittedColumns - 1) * mHorizontalSpacing > gridWidth) {  
  161.                             numFittedColumns--;  
  162.                         } else {  
  163.                             break;  
  164.                         }  
  165.                     }  
  166.                 }  
  167.             } else {  
  168.                 numFittedColumns = 2;  
  169.             }  
  170.             mNumColumns = numFittedColumns;  
  171.         }  
  172.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  173.     }  
  174.   
  175.     @Override  
  176.     protected void onDraw(Canvas canvas) {  
  177.         super.onDraw(canvas);  
  178.         initRecord();  
  179.     }  
  180.   
  181.     /** 
  182.      * 初始化坐标数据 
  183.      */  
  184.     private void initRecord() {  
  185.         gridRect.left = this.getLeft();  
  186.         gridRect.right = this.getRight();  
  187.         gridRect.top = this.getTop();  
  188.         gridRect.bottom = this.getBottom();  
  189.         if (mTrashView != null) {  
  190.             trashRect.left = mTrashView.getLeft();  
  191.             trashRect.right = mTrashView.getRight();  
  192.             trashRect.bottom = mTrashView.getBottom();  
  193.             trashRect.top = mTrashView.getTop();  
  194.         }  
  195.     }  
  196.   
  197.     /******************** preference method ********************/  
  198.   
  199.     @Override  
  200.     public void setAdapter(ListAdapter adapter) {  
  201.         super.setAdapter(adapter);  
  202.   
  203.         if (adapter instanceof DragGridBaseAdapter) {  
  204.             mDragAdapter = (DragGridBaseAdapter) adapter;  
  205.         } else {  
  206.             throw new IllegalStateException(  
  207.                     "the adapter must be implements DragGridAdapter");  
  208.         }  
  209.     }  
  210.   
  211.     @Override  
  212.     public void setNumColumns(int numColumns) {  
  213.         super.setNumColumns(numColumns);  
  214.         mNumColumnsSet = true;  
  215.         this.mNumColumns = numColumns;  
  216.     }  
  217.   
  218.     @Override  
  219.     public void setColumnWidth(int columnWidth) {  
  220.         super.setColumnWidth(columnWidth);  
  221.         mColumnWidth = columnWidth;  
  222.     }  
  223.   
  224.     @Override  
  225.     public void setHorizontalSpacing(int horizontalSpacing) {  
  226.         super.setHorizontalSpacing(horizontalSpacing);  
  227.         this.mHorizontalSpacing = horizontalSpacing;  
  228.     }  
  229.   
  230.     /** 
  231.      * 设置响应拖拽的毫秒数,默认是700毫秒 
  232.      *  
  233.      * @param dragResponseMS 
  234.      */  
  235.     public void setDragResponseMS(long dragResponseMS) {  
  236.         this.dragResponseMS = dragResponseMS;  
  237.     }  
  238.   
  239.     public void setOnDeleteListener(OnDeleteListener l) {  
  240.         this.deleteListener = l;  
  241.     }  
  242.   
  243.     public void setTrashView(View trashView) {  
  244.         this.mTrashView = trashView;  
  245.     }  
  246.   
  247.     public void setDragEnable(boolean isDrag) {  
  248.         this.canDrag = isDrag;  
  249.         if (canDrag) {  
  250.             mHandler.removeCallbacks(mLongClickRunnable);  
  251.         }  
  252.     }  
  253.   
  254.     /******************** touch method ********************/  
  255.   
  256.     @Override  
  257.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  258.         if (canDrag) {  
  259.             switch (ev.getAction()) {  
  260.             case MotionEvent.ACTION_DOWN:  
  261.                 mDownX = (int) ev.getX();  
  262.                 mDownY = (int) ev.getY();  
  263.                 moveRect.left = mDownX - MOVE_OFFSET;  
  264.                 moveRect.right = mDownX + MOVE_OFFSET;  
  265.                 moveRect.top = mDownY - MOVE_OFFSET;  
  266.                 moveRect.bottom = mDownY + MOVE_OFFSET;  
  267.   
  268.                 // 根据按下的X,Y坐标获取所点击item的position  
  269.                 mDragPosition = pointToPosition(mDownX, mDownY);  
  270.   
  271.                 if (mDragPosition == AdapterView.INVALID_POSITION) {  
  272.                     return super.dispatchTouchEvent(ev);  
  273.                 }  
  274.   
  275.                 // 使用Handler延迟dragResponseMS执行mLongClickRunnable  
  276.                 mHandler.postDelayed(mLongClickRunnable, dragResponseMS);  
  277.   
  278.                 // 根据position获取该item所对应的View  
  279.                 mStartDragItemView = getChildAt(mDragPosition  
  280.                         - getFirstVisiblePosition());  
  281.   
  282.                 mPoint2ItemTop = mDownY - mStartDragItemView.getTop();  
  283.                 mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft();  
  284.   
  285.                 mOffset2Top = (int) (ev.getRawY() - mDownY);  
  286.                 mOffset2Left = (int) (ev.getRawX() - mDownX);  
  287.   
  288.                 // 获取DragGridView自动向上滚动的偏移量,小于这个值,DragGridView向下滚动  
  289.                 mDownScrollBorder = getHeight() / 5;  
  290.                 // 大于这个值,DragGridView向上滚动  
  291.                 mUpScrollBorder = getHeight() * 4 / 5;  
  292.   
  293.                 // 开启mDragItemView绘图缓存  
  294.                 mStartDragItemView.setDrawingCacheEnabled(true);  
  295.                 // 获取mDragItemView在缓存中的Bitmap对象  
  296.                 mDragBitmap = Bitmap.createBitmap(mStartDragItemView  
  297.                         .getDrawingCache());  
  298.                 // 这一步很关键,释放绘图缓存,避免出现重复的镜像  
  299.                 mStartDragItemView.destroyDrawingCache();  
  300.                 break;  
  301.             case MotionEvent.ACTION_MOVE:  
  302.                 // 如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable  
  303.                 if (!isTouchInItem(moveRect, ev.getX(), ev.getY())) {  
  304.                     mHandler.removeCallbacks(mLongClickRunnable);  
  305.                 }  
  306.                 break;  
  307.             case MotionEvent.ACTION_UP:  
  308.                 mHandler.removeCallbacks(mScrollRunnable);  
  309.                 mHandler.removeCallbacks(mLongClickRunnable);  
  310.                 if (moved && getAdapter().getCount() > 0) {  
  311.                     mHandler.sendEmptyMessage(HANDLE_FINISH);  
  312.                 } else {  
  313.                     mHandler.sendEmptyMessage(HANDLE_CANCLE);  
  314.                 }  
  315.                 moved = false;  
  316.                 break;  
  317.             }  
  318.         }  
  319.         return super.dispatchTouchEvent(ev);  
  320.     }  
  321.   
  322.     @Override  
  323.     public boolean onTouchEvent(MotionEvent ev) {  
  324.         if (isDrag && canDrag && mDragImageView != null) {  
  325.             switch (ev.getAction()) {  
  326.             case MotionEvent.ACTION_DOWN:  
  327.                 initRecord();  
  328.                 break;  
  329.             case MotionEvent.ACTION_MOVE:  
  330.                 moveX = (int) ev.getX();  
  331.                 moveY = (int) ev.getY();  
  332.   
  333.                 onDragItem(moveX, moveY);// 拖动item  
  334.   
  335.                 if (mTrashView != null) {  
  336.                     if (inTrash(moveX, moveY)) {  
  337.                         mTrashView.setScaleX(1.7f);  
  338.                         mTrashView.setScaleY(1.7f);  
  339.                     } else {  
  340.                         mTrashView.setScaleX(1f);  
  341.                         mTrashView.setScaleY(1f);  
  342.                     }  
  343.                 }  
  344.                 break;  
  345.             case MotionEvent.ACTION_UP:  
  346.                 onStopDrag();  
  347.                 isDrag = false;  
  348.                 if (deleteListener != null && inTrash(ev.getX(), ev.getY())) {  
  349.                     deleteListener.onDelete(mDragPosition);  
  350.                 }  
  351.                 break;  
  352.             }  
  353.             return true;  
  354.         }  
  355.         return super.onTouchEvent(ev);  
  356.     }  
  357.   
  358.     /** 
  359.      * 是否点击在GridView的item上面 
  360.      *  
  361.      * @param itemView 
  362.      * @param x 
  363.      * @param y 
  364.      * @return 
  365.      */  
  366.     private boolean isTouchInItem(TouchRect moveRect, float x, float y) {  
  367.         // 防止手抖的处理,如果是横向在item上移动是没有影响的,  
  368.         // 但是纵向由于外层上下拉控件还是会有影响,具体解决请看NoteBookFragment类中的mSwipeRefreshLayout.setOnTouchListener方法  
  369.         if (x < moveRect.right && x > moveRect.left && y < moveRect.bottom  
  370.                 && y > moveRect.top) {  
  371.             return true;  
  372.         } else {  
  373.             return false;  
  374.         }  
  375.     }  
  376.   
  377.     /** 
  378.      * 创建拖动的镜像 
  379.      *  
  380.      * @param bitmap 
  381.      * @param downX 
  382.      *            按下的点相对父控件的X坐标 
  383.      * @param downY 
  384.      *            按下的点相对父控件的X坐标 
  385.      */  
  386.     private void createDragImage(Bitmap bitmap, int downX, int downY) {  
  387.         mWindowLayoutParams = new WindowManager.LayoutParams();  
  388.         mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 图片之外的其他地方透明  
  389.   
  390.         mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;  
  391.         mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;  
  392.         mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top  
  393.                 - mStatusHeight;  
  394.         mWindowLayoutParams.alpha = 0.55f; // 透明度  
  395.   
  396.         mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  397.         mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
  398.         mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
  399.                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;  
  400.   
  401.         mDragImageView = new ImageView(getContext());  
  402.         mDragImageView.setImageBitmap(bitmap);  
  403.         mWindowManager.addView(mDragImageView, mWindowLayoutParams);  
  404.     }  
  405.   
  406.     /** 
  407.      * 从界面上面移动拖动镜像 
  408.      */  
  409.     private void removeDragImage() {  
  410.         if (mDragImageView != null) {  
  411.             mWindowManager.removeView(mDragImageView);  
  412.             mDragImageView = null;  
  413.         }  
  414.     }  
  415.   
  416.     /** 
  417.      * 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动 
  418.      *  
  419.      * @param x 
  420.      * @param y 
  421.      */  
  422.     private void onDragItem(int moveX, int moveY) {  
  423.         mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left;  
  424.         mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top  
  425.                 - mStatusHeight;  
  426.         mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); // 更新镜像的位置  
  427.         onSwapItem(moveX, moveY);  
  428.         // GridView自动滚动  
  429.         mHandler.post(mScrollRunnable);  
  430.     }  
  431.   
  432.     /** 
  433.      * 手指当前处于垃圾桶图标上 
  434.      *  
  435.      * @param x 
  436.      * @param y 
  437.      * @return 
  438.      */  
  439.     private boolean inTrash(float x, float y) {  
  440.         x += gridRect.left;  
  441.         y += gridRect.top;  
  442.         if (x > trashRect.left && x < trashRect.right && y > trashRect.top  
  443.                 && y < trashRect.bottom) {  
  444.             if (mHandler != null && mScrollRunnable != null) {  
  445.                 mHandler.removeCallbacks(mScrollRunnable);  
  446.             }  
  447.             if (mDragImageView != null) {  
  448.                 mDragImageView.setScaleX(0.6f);  
  449.                 mDragImageView.setScaleY(0.6f);  
  450.             }  
  451.             return true;  
  452.         } else {  
  453.             if (mDragImageView != null) {  
  454.                 mDragImageView.setScaleX(1f);  
  455.                 mDragImageView.setScaleY(1f);  
  456.             }  
  457.             return false;  
  458.         }  
  459.     }  
  460.   
  461.     /** 
  462.      * 当moveY的值大于向上滚动的边界值,触发GridView自动向上滚动 当moveY的值小于向下滚动的边界值,触发GridView自动向下滚动 
  463.      * 否则不进行滚动 
  464.      */  
  465.     private final Runnable mScrollRunnable = new Runnable() {  
  466.   
  467.         @Override  
  468.         public void run() {  
  469.             int scrollY;  
  470.             if (getFirstVisiblePosition() == 0  
  471.                     || getLastVisiblePosition() == getCount() - 1) {  
  472.                 mHandler.removeCallbacks(mScrollRunnable);  
  473.             }  
  474.   
  475.             if (moveY > mUpScrollBorder) {  
  476.                 scrollY = speed;  
  477.                 mHandler.postDelayed(mScrollRunnable, 25);  
  478.             } else if (moveY < mDownScrollBorder) {  
  479.                 scrollY = -speed;  
  480.                 mHandler.postDelayed(mScrollRunnable, 25);  
  481.             } else {  
  482.                 scrollY = 0;  
  483.                 mHandler.removeCallbacks(mScrollRunnable);  
  484.             }  
  485.   
  486.             smoothScrollBy(scrollY, 10);  
  487.         }  
  488.     };  
  489.   
  490.     /** 
  491.      * 交换item,并且控制item之间的显示与隐藏效果 
  492.      *  
  493.      * @param moveX 
  494.      * @param moveY 
  495.      */  
  496.     private void onSwapItem(int moveX, int moveY) {  
  497.         // 获取我们手指移动到的那个item的position  
  498.   
  499.         final int tempPosition = pointToPosition(moveX, moveY);  
  500.   
  501.         // 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换  
  502.   
  503.         if (tempPosition != mDragPosition  
  504.                 && tempPosition != AdapterView.INVALID_POSITION  
  505.                 && mAnimationEnd) {  
  506.             mDragAdapter.reorderItems(mDragPosition, tempPosition);  
  507.             mDragAdapter.setHideItem(tempPosition);  
  508.   
  509.             final ViewTreeObserver observer = getViewTreeObserver();  
  510.             observer.addOnPreDrawListener(new OnPreDrawListener() {  
  511.   
  512.                 @Override  
  513.                 public boolean onPreDraw() {  
  514.                     observer.removeOnPreDrawListener(this);  
  515.                     animateReorder(mDragPosition, tempPosition);  
  516.                     mDragPosition = tempPosition;  
  517.                     return true;  
  518.                 }  
  519.             });  
  520.   
  521.         }  
  522.     }  
  523.   
  524.     /** 
  525.      * 创建移动动画 
  526.      *  
  527.      * @param view 
  528.      * @param startX 
  529.      * @param endX 
  530.      * @param startY 
  531.      * @param endY 
  532.      * @return 
  533.      */  
  534.     private AnimatorSet createTranslationAnimations(View view, float startX,  
  535.             float endX, float startY, float endY) {  
  536.         ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX",  
  537.                 startX, endX);  
  538.         ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY",  
  539.                 startY, endY);  
  540.         AnimatorSet animSetXY = new AnimatorSet();  
  541.         animSetXY.playTogether(animX, animY);  
  542.         return animSetXY;  
  543.     }  
  544.   
  545.     /** 
  546.      * item的交换动画效果 
  547.      *  
  548.      * @param oldPosition 
  549.      * @param newPosition 
  550.      */  
  551.     private void animateReorder(final int oldPosition, final int newPosition) {  
  552.         boolean isForward = newPosition > oldPosition;  
  553.         List<Animator> resultList = new LinkedList<Animator>();  
  554.         if (isForward) {  
  555.             for (int pos = oldPosition; pos < newPosition; pos++) {  
  556.                 View view = getChildAt(pos - getFirstVisiblePosition());  
  557.                 if (view == null) {  
  558.                     continue;  
  559.                 }  
  560.                 if ((pos + 1) % mNumColumns == 0) {  
  561.                     resultList.add(createTranslationAnimations(view,  
  562.                             -view.getWidth() * (mNumColumns - 1), 0,  
  563.                             view.getHeight(), 0));  
  564.                 } else {  
  565.                     resultList.add(createTranslationAnimations(view,  
  566.                             view.getWidth(), 000));  
  567.                 }  
  568.             }  
  569.         } else {  
  570.             for (int pos = oldPosition; pos > newPosition; pos--) {  
  571.                 View view = getChildAt(pos - getFirstVisiblePosition());  
  572.                 if ((pos + mNumColumns) % mNumColumns == 0) {  
  573.                     resultList.add(createTranslationAnimations(view,  
  574.                             view.getWidth() * (mNumColumns - 1), 0,  
  575.                             -view.getHeight(), 0));  
  576.                 } else {  
  577.                     resultList.add(createTranslationAnimations(view,  
  578.                             -view.getWidth(), 000));  
  579.                 }  
  580.             }  
  581.         }  
  582.   
  583.         AnimatorSet resultSet = new AnimatorSet();  
  584.         resultSet.playTogether(resultList);  
  585.         resultSet.setDuration(300);  
  586.         resultSet.setInterpolator(new AccelerateDecelerateInterpolator());  
  587.         resultSet.addListener(new AnimatorListenerAdapter() {  
  588.             @Override  
  589.             public void onAnimationStart(Animator animation) {  
  590.                 mAnimationEnd = false;  
  591.             }  
  592.   
  593.             @Override  
  594.             public void onAnimationEnd(Animator animation) {  
  595.                 mAnimationEnd = true;  
  596.             }  
  597.         });  
  598.         resultSet.start();  
  599.     }  
  600.   
  601.     /** 
  602.      * 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除 
  603.      */  
  604.     private void onStopDrag() {  
  605.         View view = getChildAt(mDragPosition - getFirstVisiblePosition());  
  606.         if (view != null) {  
  607.             view.setVisibility(View.VISIBLE);  
  608.         }  
  609.         mDragAdapter.setHideItem(-1);  
  610.         removeDragImage();  
  611.   
  612.         if (mTrashView != null) {  
  613.             mTrashView.setScaleX(1f);  
  614.             mTrashView.setScaleY(1f);  
  615.         }  
  616.     }  
  617.   
  618.     public void setOnMoveListener(OnMoveListener l) {  
  619.         moveListener = l;  
  620.     }  
  621.   
  622.     public interface OnMoveListener {  
  623.         void startMove();  
  624.   
  625.         void finishMove();  
  626.   
  627.         void cancleMove();  
  628.     }  
  629.   
  630.     public interface OnDeleteListener {  
  631.         void onDelete(int position);  
  632.     }  
  633.   
  634.     public interface DragGridBaseAdapter {  
  635.         /** 
  636.          * 移动时回调 
  637.          */  
  638.         public void reorderItems(int oldPosition, int newPosition);  
  639.   
  640.         /** 
  641.          * 隐藏时回调 
  642.          */  
  643.         public void setHideItem(int hidePosition);  
  644.     }  
  645.   
  646.     private class TouchRect {  
  647.         int top;  
  648.         int bottom;  
  649.         int left;  
  650.         int right;  
  651.     }  
  652. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值