Android 可拖拽的GridView效果实现, 长按可拖拽和item实时交换

来源:互联网 发布:simd编程 编辑:程序博客网 时间:2024/05/23 21:02


转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17718579),请尊重他人的辛勤劳动成果,谢谢!

在Android开发中,我们常常用到ListView和GridView,而有的时候系统的ListView,GridView并不能满足我们的需求,所以我们需要自己定义一个ListView或者GridView,我的上一篇文章中就是自定义的一个左右滑动删除item的例子,大家有兴趣的可以去看看Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果,今天这篇文章就给大家来自定义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的镜像
看完上面的这些思路你是不是找到了些感觉了呢,心里痒痒的想动手试试吧,好吧,接下来就带大家根据思路来实现可拖拽的GridView,新建一个项目就叫DragGridView
新建一个类DragGridView继承GridView,先来看看DragGridView的代码,然后在根据代码进行相关的讲解

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.draggridview;  
  2.   
  3. import android.annotation.SuppressLint;  
  4. import android.app.Activity;  
  5. import android.content.Context;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.PixelFormat;  
  8. import android.graphics.Rect;  
  9. import android.os.Handler;  
  10. import android.os.Vibrator;  
  11. import android.text.GetChars;  
  12. import android.util.AttributeSet;  
  13. import android.view.Gravity;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16. import android.view.WindowManager;  
  17. import android.widget.AdapterView;  
  18. import android.widget.GridView;  
  19. import android.widget.ImageView;  
  20.   
  21. /** 
  22.  * @blog http://blog.csdn.net/xiaanming  
  23.  *  
  24.  * @author xiaanming 
  25.  * 
  26.  */  
  27. @SuppressLint("NewApi")  
  28. public class DragGridView extends GridView{  
  29.     /** 
  30.      * DragGridView的item长按响应的时间, 默认是1000毫秒,也可以自行设置 
  31.      */  
  32.     private long dragResponseMS = 1000;  
  33.       
  34.     /** 
  35.      * 是否可以拖拽,默认不可以 
  36.      */  
  37.     private boolean isDrag = false;  
  38.       
  39.     private int mDownX;  
  40.     private int mDownY;  
  41.     private int moveX;  
  42.     private int moveY;  
  43.     /** 
  44.      * 正在拖拽的position 
  45.      */  
  46.     private int mDragPosition;  
  47.       
  48.     /** 
  49.      * 刚开始拖拽的item对应的View 
  50.      */  
  51.     private View mStartDragItemView = null;  
  52.       
  53.     /** 
  54.      * 用于拖拽的镜像,这里直接用一个ImageView 
  55.      */  
  56.     private ImageView mDragImageView;  
  57.       
  58.     /** 
  59.      * 震动器 
  60.      */  
  61.     private Vibrator mVibrator;  
  62.       
  63.     private WindowManager mWindowManager;  
  64.     /** 
  65.      * item镜像的布局参数 
  66.      */  
  67.     private WindowManager.LayoutParams mWindowLayoutParams;  
  68.       
  69.     /** 
  70.      * 我们拖拽的item对应的Bitmap 
  71.      */  
  72.     private Bitmap mDragBitmap;  
  73.       
  74.     /** 
  75.      * 按下的点到所在item的上边缘的距离 
  76.      */  
  77.     private int mPoint2ItemTop ;   
  78.       
  79.     /** 
  80.      * 按下的点到所在item的左边缘的距离 
  81.      */  
  82.     private int mPoint2ItemLeft;  
  83.       
  84.     /** 
  85.      * DragGridView距离屏幕顶部的偏移量 
  86.      */  
  87.     private int mOffset2Top;  
  88.       
  89.     /** 
  90.      * DragGridView距离屏幕左边的偏移量 
  91.      */  
  92.     private int mOffset2Left;  
  93.       
  94.     /** 
  95.      * 状态栏的高度 
  96.      */  
  97.     private int mStatusHeight;   
  98.       
  99.     /** 
  100.      * DragGridView自动向下滚动的边界值 
  101.      */  
  102.     private int mDownScrollBorder;  
  103.       
  104.     /** 
  105.      * DragGridView自动向上滚动的边界值 
  106.      */  
  107.     private int mUpScrollBorder;  
  108.       
  109.     /** 
  110.      * DragGridView自动滚动的速度 
  111.      */  
  112.     private static final int speed = 80;  
  113.       
  114.     /** 
  115.      * item发生变化回调的接口 
  116.      */  
  117.     private OnChanageListener onChanageListener;  
  118.       
  119.       
  120.       
  121.     public DragGridView(Context context) {  
  122.         this(context, null);  
  123.     }  
  124.       
  125.     public DragGridView(Context context, AttributeSet attrs) {  
  126.         this(context, attrs, 0);  
  127.     }  
  128.   
  129.     public DragGridView(Context context, AttributeSet attrs, int defStyle) {  
  130.         super(context, attrs, defStyle);  
  131.         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);  
  132.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  133.         mStatusHeight = getStatusHeight(context); //获取状态栏的高度  
  134.     }  
  135.       
  136.     private Handler mHandler = new Handler();  
  137.       
  138.     //用来处理是否为长按的Runnable  
  139.     private Runnable mLongClickRunnable = new Runnable() {  
  140.           
  141.         @Override  
  142.         public void run() {  
  143.             isDrag = true//设置可以拖拽  
  144.             mVibrator.vibrate(50); //震动一下  
  145.               
  146.             mStartDragItemView.setVisibility(View.INVISIBLE);//隐藏该item  
  147.               
  148.             //根据我们按下的点显示item镜像  
  149.             createDragImage(mDragBitmap, mDownX, mDownY);  
  150.               
  151.               
  152.         }  
  153.     };  
  154.       
  155.     /** 
  156.      * 设置回调接口 
  157.      * @param onChanageListener 
  158.      */  
  159.     public void setOnChangeListener(OnChanageListener onChanageListener){  
  160.         this.onChanageListener = onChanageListener;  
  161.     }  
  162.       
  163.     /** 
  164.      * 设置响应拖拽的毫秒数,默认是1000毫秒 
  165.      * @param dragResponseMS 
  166.      */  
  167.     public void setDragResponseMS(long dragResponseMS) {  
  168.         this.dragResponseMS = dragResponseMS;  
  169.     }  
  170.   
  171.     @Override  
  172.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  173.         switch(ev.getAction()){  
  174.         case MotionEvent.ACTION_DOWN:  
  175.             //使用Handler延迟dragResponseMS执行mLongClickRunnable  
  176.             mHandler.postDelayed(mLongClickRunnable, dragResponseMS);  
  177.               
  178.             mDownX = (int) ev.getX();  
  179.             mDownY = (int) ev.getY();  
  180.               
  181.             //根据按下的X,Y坐标获取所点击item的position  
  182.             mDragPosition = pointToPosition(mDownX, mDownY);  
  183.               
  184.             if(mDragPosition == AdapterView.INVALID_POSITION){  
  185.                 return super.dispatchTouchEvent(ev);  
  186.             }  
  187.               
  188.             //根据position获取该item所对应的View  
  189.             mStartDragItemView = getChildAt(mDragPosition - getFirstVisiblePosition());  
  190.               
  191.             //下面这几个距离大家可以参考我的博客上面的图来理解下  
  192.             mPoint2ItemTop = mDownY - mStartDragItemView.getTop();  
  193.             mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft();  
  194.               
  195.             mOffset2Top = (int) (ev.getRawY() - mDownY);  
  196.             mOffset2Left = (int) (ev.getRawX() - mDownX);  
  197.               
  198.             //获取DragGridView自动向上滚动的偏移量,小于这个值,DragGridView向下滚动  
  199.             mDownScrollBorder = getHeight() /4;  
  200.             //获取DragGridView自动向下滚动的偏移量,大于这个值,DragGridView向上滚动  
  201.             mUpScrollBorder = getHeight() * 3/4;  
  202.               
  203.               
  204.               
  205.             //开启mDragItemView绘图缓存  
  206.             mStartDragItemView.setDrawingCacheEnabled(true);  
  207.             //获取mDragItemView在缓存中的Bitmap对象  
  208.             mDragBitmap = Bitmap.createBitmap(mStartDragItemView.getDrawingCache());  
  209.             //这一步很关键,释放绘图缓存,避免出现重复的镜像  
  210.             mStartDragItemView.destroyDrawingCache();  
  211.               
  212.               
  213.             break;  
  214.         case MotionEvent.ACTION_MOVE:  
  215.             int moveX = (int)ev.getX();  
  216.             int moveY = (int) ev.getY();  
  217.               
  218.             //如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable  
  219.             if(!isTouchInItem(mStartDragItemView, moveX, moveY)){  
  220.                 mHandler.removeCallbacks(mLongClickRunnable);  
  221.             }  
  222.             break;  
  223.         case MotionEvent.ACTION_UP:  
  224.             mHandler.removeCallbacks(mLongClickRunnable);  
  225.             mHandler.removeCallbacks(mScrollRunnable);  
  226.             break;  
  227.         }  
  228.         return super.dispatchTouchEvent(ev);  
  229.     }  
  230.   
  231.       
  232.     /** 
  233.      * 是否点击在GridView的item上面 
  234.      * @param itemView 
  235.      * @param x 
  236.      * @param y 
  237.      * @return 
  238.      */  
  239.     private boolean isTouchInItem(View dragView, int x, int y){  
  240.         int leftOffset = dragView.getLeft();  
  241.         int topOffset = dragView.getTop();  
  242.         if(x < leftOffset || x > leftOffset + dragView.getWidth()){  
  243.             return false;  
  244.         }  
  245.           
  246.         if(y < topOffset || y > topOffset + dragView.getHeight()){  
  247.             return false;  
  248.         }  
  249.           
  250.         return true;  
  251.     }  
  252.       
  253.       
  254.   
  255.     @Override  
  256.     public boolean onTouchEvent(MotionEvent ev) {  
  257.         if(isDrag && mDragImageView != null){  
  258.             switch(ev.getAction()){  
  259.             case MotionEvent.ACTION_MOVE:  
  260.                 moveX = (int) ev.getX();  
  261.                 moveY = (int) ev.getY();  
  262.                 //拖动item  
  263.                 onDragItem(moveX, moveY);  
  264.                 break;  
  265.             case MotionEvent.ACTION_UP:  
  266.                 onStopDrag();  
  267.                 isDrag = false;  
  268.                 break;  
  269.             }  
  270.             return true;  
  271.         }  
  272.         return super.onTouchEvent(ev);  
  273.     }  
  274.       
  275.       
  276.     /** 
  277.      * 创建拖动的镜像 
  278.      * @param bitmap  
  279.      * @param downX 
  280.      *          按下的点相对父控件的X坐标 
  281.      * @param downY 
  282.      *          按下的点相对父控件的X坐标 
  283.      */  
  284.     private void createDragImage(Bitmap bitmap, int downX , int downY){  
  285.         mWindowLayoutParams = new WindowManager.LayoutParams();  
  286.         mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; //图片之外的其他地方透明  
  287.         mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;  
  288.         mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;  
  289.         mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight;  
  290.         mWindowLayoutParams.alpha = 0.55f; //透明度  
  291.         mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;    
  292.         mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;    
  293.         mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE    
  294.                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ;  
  295.             
  296.         mDragImageView = new ImageView(getContext());    
  297.         mDragImageView.setImageBitmap(bitmap);    
  298.         mWindowManager.addView(mDragImageView, mWindowLayoutParams);    
  299.     }  
  300.       
  301.     /** 
  302.      * 从界面上面移动拖动镜像 
  303.      */  
  304.     private void removeDragImage(){  
  305.         if(mDragImageView != null){  
  306.             mWindowManager.removeView(mDragImageView);  
  307.             mDragImageView = null;  
  308.         }  
  309.           
  310.     }  
  311.       
  312.       
  313.       
  314.     /** 
  315.      * 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动 
  316.      * @param x 
  317.      * @param y 
  318.      */  
  319.     private void onDragItem(int moveX, int moveY){  
  320.         mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left;  
  321.         mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top - mStatusHeight;  
  322.         mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); //更新镜像的位置  
  323.         onSwapItem(moveX, moveY);  
  324.           
  325.         //GridView自动滚动  
  326.         mHandler.post(mScrollRunnable);  
  327.     }  
  328.       
  329.       
  330.     /** 
  331.      * 当moveY的值大于向上滚动的边界值,触发GridView自动向上滚动 
  332.      * 当moveY的值小于向下滚动的边界值,触犯GridView自动向下滚动 
  333.      * 否则不进行滚动 
  334.      */  
  335.     private Runnable mScrollRunnable = new Runnable() {  
  336.           
  337.         @Override  
  338.         public void run() {  
  339.             int scrollY;  
  340.             if(moveY > mUpScrollBorder){  
  341.                  scrollY = -speed;  
  342.                  mHandler.postDelayed(mScrollRunnable, 25);  
  343.             }else if(moveY < mDownScrollBorder){  
  344.                 scrollY = speed;  
  345.                  mHandler.postDelayed(mScrollRunnable, 25);  
  346.             }else{  
  347.                 scrollY = 0;  
  348.                 mHandler.removeCallbacks(mScrollRunnable);  
  349.             }  
  350.               
  351.             //当我们的手指到达GridView向上或者向下滚动的偏移量的时候,可能我们手指没有移动,但是DragGridView在自动的滚动  
  352.             //所以我们在这里调用下onSwapItem()方法来交换item  
  353.             onSwapItem(moveX, moveY);  
  354.               
  355.             View view = getChildAt(mDragPosition - getFirstVisiblePosition());  
  356.             //实现GridView的自动滚动  
  357.             smoothScrollToPositionFromTop(mDragPosition, view.getTop() + scrollY);  
  358.         }  
  359.     };  
  360.       
  361.       
  362.     /** 
  363.      * 交换item,并且控制item之间的显示与隐藏效果 
  364.      * @param moveX 
  365.      * @param moveY 
  366.      */  
  367.     private void onSwapItem(int moveX, int moveY){  
  368.         //获取我们手指移动到的那个item的position  
  369.         int tempPosition = pointToPosition(moveX, moveY);  
  370.           
  371.         //假如tempPosition 改变了并且tempPosition不等于-1,则进行交换  
  372.         if(tempPosition != mDragPosition && tempPosition != AdapterView.INVALID_POSITION){  
  373.             getChildAt(tempPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);//拖动到了新的item,新的item隐藏掉  
  374.             getChildAt(mDragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);//之前的item显示出来  
  375.               
  376.             if(onChanageListener != null){  
  377.                 onChanageListener.onChange(mDragPosition, tempPosition);  
  378.             }  
  379.               
  380.             mDragPosition = tempPosition;  
  381.         }  
  382.     }  
  383.       
  384.       
  385.     /** 
  386.      * 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除 
  387.      */  
  388.     private void onStopDrag(){  
  389.         getChildAt(mDragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);  
  390.         removeDragImage();  
  391.     }  
  392.       
  393.     /** 
  394.      * 获取状态栏的高度 
  395.      * @param context 
  396.      * @return 
  397.      */  
  398.     private static int getStatusHeight(Context context){  
  399.         int statusHeight = 0;  
  400.         Rect localRect = new Rect();  
  401.         ((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(localRect);  
  402.         statusHeight = localRect.top;  
  403.         if (0 == statusHeight){  
  404.             Class<?> localClass;  
  405.             try {  
  406.                 localClass = Class.forName("com.android.internal.R$dimen");  
  407.                 Object localObject = localClass.newInstance();  
  408.                 int i5 = Integer.parseInt(localClass.getField("status_bar_height").get(localObject).toString());  
  409.                 statusHeight = context.getResources().getDimensionPixelSize(i5);  
  410.             } catch (Exception e) {  
  411.                 e.printStackTrace();  
  412.             }   
  413.         }  
  414.         return statusHeight;  
  415.     }  
  416.       
  417.       
  418.     /** 
  419.      *  
  420.      * @author xiaanming 
  421.      * 
  422.      */  
  423.     public interface OnChanageListener{  
  424.           
  425.         /** 
  426.          * 当item交换位置的时候回调的方法,我们只需要在该方法中实现数据的交换即可 
  427.          * @param form 
  428.          *          开始的position 
  429.          * @param to  
  430.          *          拖拽到的position 
  431.          */  
  432.         public void onChange(int form, int to);  
  433.     }  
  434. }  

首先看DragGridView的事件分发方法,不了解Android事件分发的可以先去了解下,Android事件分发对于自定义控件很重要,简单说下,当我们点击DragGridView的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  DragGridView的上边缘到屏幕上边缘的距离,如上图的3号线,这个距离包裹状态栏,标题栏,或者一些在DragGridView上面的布局的高度,这个很重要我们现实Item镜像需要用到
  • mOffset2Left DragGridView的左边缘到屏幕左边缘的距离,如上图的4号线,我这个Demo的这个距离为0,因为我设置DragGridView的宽度为充满屏幕,但是我们要考虑假如DragGridView与屏幕左边缘设置了间隙或者左边有其他的布局的情形
  • mDownScrollBorder 这个距离表示当DragGridView的item过多的时候,手机一屏显示不完全,我们拖动Item镜像到这个高度的时候,DragGridView自动向下滚动,如上图的5号线
  • .mUpScrollBorder 这个和mDownScrollBorder相反,当我们大于这个高度的时候,DragGridView自动向上滚动,如上图的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吧,先看主界面布局,只有我们自定义的一个DragGridView

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <com.example.draggridview.DragGridView  
  7.         android:id="@+id/dragGridView"  
  8.         android:listSelector="@android:color/transparent"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent"  
  11.         android:cacheColorHint="@android:color/transparent"  
  12.         android:verticalSpacing="10dip"  
  13.         android:horizontalSpacing="10dip"  
  14.         android:stretchMode="columnWidth"    
  15.         android:gravity="center"   
  16.         android:numColumns="3" >  
  17.     </com.example.draggridview.DragGridView>  
  18.   
  19. </RelativeLayout>  
接下来我们看看DragGridView的item的布局,上面一个ImageView下面一个TextView
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:background="@android:color/transparent" >  
  6.   
  7.     <ImageView  
  8.         android:id="@+id/item_image"  
  9.         android:scaleType="centerCrop"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:layout_centerHorizontal="true" >  
  13.     </ImageView>  
  14.   
  15.     <TextView  
  16.         android:id="@+id/item_text"  
  17.         android:layout_width="wrap_content"  
  18.         android:layout_height="wrap_content"  
  19.         android:layout_below="@+id/item_image"  
  20.         android:layout_centerHorizontal="true" >  
  21.     </TextView>  
  22.   
  23. </RelativeLayout>  
布局搞定了我们就来看看主页面MainActivity的代码吧
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.draggridview;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collections;  
  5. import java.util.HashMap;  
  6. import java.util.List;  
  7.   
  8. import android.app.Activity;  
  9. import android.os.Bundle;  
  10. import android.widget.SimpleAdapter;  
  11.   
  12. import com.example.draggridview.DragGridView.OnChanageListener;  
  13.   
  14. /** 
  15.  * @blog http://blog.csdn.net/xiaanming  
  16.  *  
  17.  * @author xiaanming 
  18.  * 
  19.  */  
  20. public class MainActivity extends Activity {  
  21.     private List<HashMap<String, Object>> dataSourceList = new ArrayList<HashMap<String, Object>>();  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);  
  27.   
  28.         DragGridView mDragGridView = (DragGridView) findViewById(R.id.dragGridView);  
  29.         for (int i = 0; i < 30; i++) {  
  30.             HashMap<String, Object> itemHashMap = new HashMap<String, Object>();  
  31.             itemHashMap.put("item_image",R.drawable.com_tencent_open_notice_msg_icon_big);  
  32.             itemHashMap.put("item_text""拖拽 " + Integer.toString(i));  
  33.             dataSourceList.add(itemHashMap);  
  34.         }  
  35.           
  36.   
  37.         final SimpleAdapter mSimpleAdapter = new SimpleAdapter(this, dataSourceList,  
  38.                 R.layout.grid_item, new String[] { "item_image""item_text" },  
  39.                 new int[] { R.id.item_image, R.id.item_text });  
  40.           
  41.         mDragGridView.setAdapter(mSimpleAdapter);  
  42.           
  43.         mDragGridView.setOnChangeListener(new OnChanageListener() {  
  44.               
  45.             @Override  
  46.             public void onChange(int from, int to) {  
  47.                 HashMap<String, Object> temp = dataSourceList.get(from);  
  48.                 //直接交互item  
  49. //              dataSourceList.set(from, dataSourceList.get(to));  
  50. //              dataSourceList.set(to, temp);  
  51. //              dataSourceList.set(to, temp);  
  52.                   
  53.                   
  54.                 //这里的处理需要注意下  
  55.                 if(from < to){  
  56.                     for(int i=from; i<to; i++){  
  57.                         Collections.swap(dataSourceList, i, i+1);  
  58.                     }  
  59.                 }else if(from > to){  
  60.                     for(int i=from; i>to; i--){  
  61.                         Collections.swap(dataSourceList, i, i-1);  
  62.                     }  
  63.                 }  
  64.                   
  65.                 dataSourceList.set(to, temp);  
  66.                   
  67.                 mSimpleAdapter.notifyDataSetChanged();  
  68.                   
  69.                   
  70.             }  
  71.         });  
  72.           
  73.     }  
  74.   
  75. }  
这里面的代码还是比较简单,主要讲下onChange()方法,我们要为mDragGridView设置一个OnChanageListener的回调接口,在onChange()方法里面实现数据的交换逻辑,第一个参数from为item开始的位置,第二个参数to为item拖拽到的位置,刚开始我使用的交换逻辑是
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. HashMap<String, Object> temp = dataSourceList.get(from);  
  2.                 //直接交互item  
  3. //              dataSourceList.set(from, dataSourceList.get(to));  
  4. //              dataSourceList.set(to, temp);  
直接交换的item的数据,然后看了下网易新闻的拖拽的GridView,他不是直接实现两个item直接的数据交换,所以将数据交换逻辑改成了下面的方式
简单说下,数据的交换逻辑,比如我们将position从5拖拽到7这个位置,我注释掉的逻辑是直接将5和7的数据交换,而后面的那种逻辑是将6的位置数据移动到5,将7的位置移动到6,然后再7显示5  6->5, 7->6, 5->7不知道大家理解了没有。
接下来我们来运行下项目,在运行之前我们不要忘了在AndroidManifest.xml里面加入震动的权限<uses-permission android:name="android.permission.VIBRATE"/>



好了,今天的讲解就到此结束,效果还不错吧,看完这篇文章你是不是觉得GridView拖拽也不是那么难实现呢?你心里是不是也大概有自己的一个思路,建议大家自己敲敲看看,可以自己去实现下ListView的拖拽实现,ListView比GridView简单些,好的学习方法不是看得懂人家的代码,而是看完代码自己根据脑海里的思路自己敲出来,所以还是鼓励大家多敲代码,不明白的同学在下面留言,我会为大家解答的!

项目源码,点击下载

上面的代码存在兼容性的问题,在4.X以上的机器运行时OK的,但是在2.X的机器运行就存在错误,原因是因为smoothScrollToPositionFromTop()方法在2.x的api中不存在,如果在2.x的机器上面运行将smoothScrollToPositionFromTop(mDragPosition, view.getTop() + scrollY)改成smoothScrollBy(-scrollY, 10); 并且将speed的值适当改小点,不然自动滚动过快

兼容2.X以上的机器源码,点击下载
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大套摩托车查车怎么办 摩托车本扣12分怎么办 蓝牙耳机上面的软套掉了怎么办 工作不想干了怎么办啊 浴室门玻璃坏了怎么办 电梯顶层安全安全距离不合格怎么办 制冰机有些孔不出水怎么办 制冰机排水孔堵住了怎么办 点我达装备抽查怎么办 空军常服大檐帽有一点变形怎么办 一般纳税人没有进项发票怎么办 汽车购买发票丢了怎么办 购买房子的发票丢了怎么办 购买的二手房发票丢失怎么办 购买空调时的发票丢失怎么办 发票购票本丢了怎么办 留学中介费贵了怎么办 车祸伤者不肯出院怎么办 增值税普通发票发票联丢失怎么办 纳税识别号错了怎么办 电子发票税号填写错误怎么办 发票抬头名称写错了怎么办 滴滴发票抬头写错了怎么办 普票抬头开错了怎么办 发票号码打错了怎么办 个人税票开错了怎么办 普票地址错了怎么办 专票税率开高了怎么办 专票税率开错了怎么办 发票姓名写错了怎么办 首付发票姓名写错怎么办 一般纳税人税率开错了怎么办 电子税票开错了怎么办 税率是6%开成3%怎么办 医院收费收错了怎么办 发票少打一个字怎么办 增值税发票抬头错了一个字怎么办 暂估入账跨年了怎么办 电子发票写错了怎么办 发票纳税人识别号写错了怎么办 电子发票忘填邮箱怎么办