time 
设为首页】【收藏本站
当前位置: 主页 > 电脑网络 > 操作系统 > 嵌入式 > Android > Android实现可拖拽的GridView效果

Android实现可拖拽的GridView效果

时间:2014-01-26 11:45 点击:6941次 字体:[ ]




在Android开发中,我们常常用到ListView和GridView,而有的时候系统的ListView,GridView并不能满足我们的需求,所以我们需要自己定义一个ListView或者GridView,我的上一篇文章中就是自定义的一个左右滑动删除item的例子,大家有兴趣的可以去看看 ,今天这篇文章就给大家来自定义GridView的控件,GridView主要是来显示网格的控件,在Android的开发中使用很普通,相对于TextView,Button这些控件来说要来的复杂些,今天给大家带来长按GridView的item,然后将其拖拽其他item上面,使得GridView的item发生交换,比较典型的就是我们的Launcher,网上有很多关于GridView的拖动的Demo,但是大部分都是相同的,而且存在一些Bug,而且大部分都是点击GridView的item然后进行拖动,或者item之间不进行实时交换,今天给大家更加详细的介绍GridView拖拽,并且将Demo做的更完美,大家更容易接受,也许很多人听到这个感觉实现起来很复杂,就关掉的这篇文章,其实告诉大家,只要知道了思路就感觉一点都不复杂了,不信大家可以接着往下看看,首先还是跟大家说说实现的思路
根据手指按下的X,Y坐标来获取我们在GridView上面点击的item
手指按下的时候使用Handler和Runnable来实现一个定时器,假如定时时间为1000毫秒,在1000毫秒内,如果手指抬起了移除定时器,没有抬起并且手指点击在GridView的item所在的区域,则表示我们长按了GridView的item
如果我们长按了item则隐藏item,然后使用WindowManager来添加一个item的镜像在屏幕用来代替刚刚隐藏的item
当我们手指在屏幕移动的时候,更新item镜像的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置
到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动
GridView交换数据,刷新界面,移除item的镜像
看完上面的这些思路你是不是找到了些感觉了呢,心里痒痒的想动手试试吧,好吧,接下来就带大家根据思路来实现可拖拽的GridView,新建一个项目就叫DragGridView
新建一个类DragGridView继承GridView,先来看看DragGridView的代码,然后在根据代码进行相关的讲解

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

 



本文地址 : http://www.fengfly.com/plus/view-214544-1.html
标签: Android GridView
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码: