说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher而实现的附属品(可以拖拽的ListView)

来源:互联网 发布:tensorflow分布式搭建 编辑:程序博客网 时间:2024/04/29 18:18
博客搬家啦——为了更好地经营博客,本人已经将博客迁移至www.ijavaboy.com。这里已经不再更新,给您带来的不便,深感抱歉!这篇文章的新地址:点击我微笑

    

   本来这一篇将写Android中Launcher是如何实现桌面上item的拖拽的,当研究了其机理之后,突然大脑发热,想实现一个可以拖拽的ListView,在理解了Launcher中item的拖拽,再来实现可以拖拽的ListView简直就是小菜一碟了。于是将此篇位于Launcher中拖拽之前,可以起到一个过渡理解的作用。只是,这里还有些不一样的地方就是Launcher上item拖拽后可以放到一个空的位置,而ListView中某个item被拖拽之后是需要交换新位置和原来位置上的item。下面,就不再废话了,直接上代码,具体需要注意的地方请看注释:

[java] view plaincopyprint?
  1. public class DragListView extends ListView{  
  2.     private static final String TAG = "DragListView";  
  3.     private static final int INVALID_POSITION = -1;  
  4.     private Bitmap mDragBitmap;  
  5.       
  6.     private View mDragView;  
  7.       
  8.     private UorderDeleteZone zone;  
  9.       
  10.     private View footer;  
  11.       
  12.     private Object srcContent;  
  13.       
  14.     private int startPosition;  
  15.       
  16.     private Paint mPaint = new Paint();  
  17.       
  18.     private float mLastMotionX;  
  19.     private float mLastMotionY;  
  20.       
  21.     private float mTouchOffsetX;  
  22.     private float mTouchOffsetY;  
  23.       
  24.     private float mBitmapOffsetX;  
  25.     private float mBitmapOffsetY;  
  26.       
  27.     private boolean mDragging = false;  
  28.       
  29.     private Rect mDragRect = new Rect(); //当拖动一个item的时候,记录拖动经过的区域  
  30.       
  31.     //是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。  
  32.     private int mScaledTouchSlop;  
  33.       
  34.     private float mTopScrollBound;//向上滑动超过这个边界的时候,上面的item向下滚动  
  35.       
  36.     private float mBottomScrollBound;//向下滑动超过这个边界的时候,下面的item开始向上滚动  
  37.       
  38.     public DragListView(Context context){  
  39.         this(context, null);  
  40.     }  
  41.       
  42.     public DragListView(Context context, AttributeSet attrs) {  
  43.         this(context, attrs, 0);  
  44.     }     
  45.   
  46.     public DragListView(Context context, AttributeSet attrs, int defStyle) {  
  47.         super(context, attrs, defStyle);  
  48.           
  49.         /** 
  50.          * 当某个item被拖拽时,将其颜色改变下, 
  51.          */  
  52.         final int srcColor = context.getResources().getColor(R.color.drag_filter);  
  53.         mPaint.setColorFilter(new PorterDuffColorFilter(srcColor, PorterDuff.Mode.SRC_ATOP));  
  54.           
  55.         //是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。  
  56.         mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
  57.           
  58.           
  59.     }  
  60.       
  61.     protected void dispatchDraw(Canvas canvas) {  
  62.         super.dispatchDraw(canvas);  
  63.         if(mDragging && mDragBitmap != null){  
  64.             final int scrollX = getScrollX();  
  65.             final int scrollY = getScrollY();  
  66.               
  67.             float left = scrollX + mLastMotionX-mTouchOffsetX-mBitmapOffsetX;  
  68.             float top = scrollY + mLastMotionY-mTouchOffsetY-mBitmapOffsetY;  
  69.             canvas.drawBitmap(mDragBitmap, left, top, mPaint);  
  70.         }  
  71.           
  72.           
  73.     }  
  74.       
  75.     /** 
  76.      * 在这个方法中判断当前用户按下事件所在的位置 
  77.      * 如果该位置在右边30dip之内,就进行拖拽 
  78.      */  
  79.     public boolean onInterceptTouchEvent(MotionEvent event){  
  80.           
  81.           
  82.         int action = event.getAction();  
  83.         final float x = event.getX();  
  84.         final float y = event.getY();  
  85.           
  86.         if(getWidth()-x > 30){  
  87.             //不是位于拖拽区域,直接返回就OK了  
  88.             return super.onInterceptTouchEvent(event);  
  89.         }         
  90.           
  91.         switch (action) {  
  92.         case MotionEvent.ACTION_DOWN:  
  93.             mLastMotionX = x;  
  94.             mLastMotionY = y;  
  95.             break;  
  96.         default:  
  97.             break;  
  98.         }  
  99.         Log.e(TAG, "touchSlop:"+mScaledTouchSlop);  
  100.         //计算滚动边界   
  101.         /** 
  102.          * 这里当向上拖拽到1/3屏幕高度的时候就滚动,但是这里如果开始拖拽的item就是位于这1/3屏幕内位置的 
  103.          * 则,取当前小的一个.当然你也就可以设置成1/3的屏幕 
  104.          */  
  105.         mTopScrollBound = Math.min(y-mScaledTouchSlop, getHeight()/3);  
  106.         mBottomScrollBound = Math.min(y + mScaledTouchSlop, getHeight()*2/3);  
  107.       
  108. //      mTopScrollBound = getHeight()/3;   
  109. //      mBottomScrollBound = getHeight()*2/3;  
  110.           
  111.         Log.e(TAG, "bound scroll:"+mTopScrollBound+","+mBottomScrollBound);  
  112.           
  113.         //获取当前事件坐标所在的item项,ListView中拖动是上下拖动的,所以,和x坐标关系不大  
  114.         int position = this.pointToPosition((int)x, (int)y);  
  115.           
  116.         if(position == INVALID_POSITION){  
  117.             return super.onInterceptTouchEvent(event);  
  118.         }  
  119.           
  120.         startPosition = position;  
  121.           
  122.         /** 
  123.          * 这里获取当前被拖拽的item,注意,因为ListView中并不是每个item都是一个View,这个View是重用的 
  124.          */  
  125.         mDragView = getChildAt(position-getFirstVisiblePosition());  
  126.         srcContent = getItemAtPosition(position);  
  127.           
  128.         if(mDragView != null){  
  129.               
  130.             mDragBitmap = createViewBitmap(mDragView);  
  131.             //当item被拖拽后,删除原来位置上的item,其实就是将Adapter中该位置的内容给删掉  
  132.             /** 
  133.              * 注意,该ListView用的Adapter是ArrayAdapter,其有remove(Object item)方法 
  134.              * 但是,经常处理ListView显示数据的应该知道,ArrayList的remove(object)方法实现了, 
  135.              * 但是我们使用ArrayAdatper的时候,如果我们的数据传递到Adapter用的是数组,那么ArrayAdapter内 
  136.              * 默认使用Arrays.asList(Object[])方法将数组转换为List对象,这样,当我们调用ArrayList的 
  137.              * removeItem方法的时候,就会出现java.lang.UnsupportedOperationException异常。这是因为 
  138.              * Arrays.asList(Object[])转换后的List是Arrays.ArrayList对象,而不是java.utils.ArrayList 
  139.              * Arrays.ArrayList并没有实现remove方法,所以,执行父类AbstractList默认的remove方法,而 
  140.              * AbstractList的remove方法就是抛出一个UnsupportedOperationException异常 
  141.              */  
  142.             removeItem(position);  
  143.               
  144.             mDragging = true;     
  145.         }  
  146.           
  147.         return true;  
  148.     }  
  149.       
  150.     @SuppressWarnings("unchecked")  
  151.     private void removeItem(int position){  
  152.         ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();  
  153.         adapter.remove(adapter.getItem(position));        
  154.     }  
  155.       
  156.     @SuppressWarnings("unchecked")  
  157.     private void addItem(int position){  
  158.   
  159.         ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();  
  160.         adapter.insert((String)srcContent, position);  
  161.     }  
  162.       
  163.     public boolean onTouchEvent(MotionEvent event){  
  164.         super.onTouchEvent(event);  
  165.           
  166.         if(!mDragging){  
  167.             return false;  
  168.         }  
  169.           
  170.           
  171.         final int action = event.getAction();  
  172.         final float x = event.getX();  
  173.         final float y = event.getY();  
  174.           
  175.         switch(action){  
  176.         case MotionEvent.ACTION_DOWN:  
  177.             //mLastMotionX = x;  
  178.             mLastMotionY = y;  
  179.             break;  
  180.         case MotionEvent.ACTION_MOVE:  
  181.             final int scrollX = getScrollX();  
  182.             final int scrollY = getScrollY();  
  183.               
  184.             int left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);  
  185.             int top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);  
  186.               
  187.             final int width = mDragBitmap.getWidth();  
  188.             final int height = mDragBitmap.getHeight();  
  189.               
  190.             mDragRect.set(left-1, top-1, width+left+1, top+height+1);  
  191.             Log.e(TAG, "每次move的距离:"+(y-mLastMotionY));  
  192.             //mLastMotionX = x;   
  193.             mLastMotionY = y;  
  194.               
  195.             left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);  
  196.             top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);             
  197.               
  198.             mDragRect.union(left-1, top-1, width+left+1, top+height+1);  
  199.   
  200.             invalidate(mDragRect);  
  201.               
  202.             int scrollHeight = 0;  
  203.               
  204.   
  205.             if(y < mTopScrollBound){  
  206.                 /** 
  207.                  * 如果当前move到的y位置为mTopScrollBound之上,则下面获取当前位置的item, 
  208.                  * 并将该item在当前位置的基础上,向下移动15个dip的偏移量,这样,当向上drag的时候 
  209.                  * 后面的item向下在滚动 
  210.                  *  
  211.                  * 向下拖拽的时候同理 
  212.                  */  
  213.                 scrollHeight = 15;  
  214.             }else if(y > mBottomScrollBound){  
  215.                 scrollHeight = -15;  
  216.             }  
  217.               
  218.               
  219.               
  220.             if(scrollHeight != 0){  
  221.                 //调用Listview方法实现滚动  
  222.                 int position = this.pointToPosition((int)x, (int)y);  
  223.                 if(position != INVALID_POSITION){  
  224.                     /** 
  225.                      * 将当前位置的item向上或者向下移动scrollHeight个单位的距离 
  226.                      */  
  227.                     setSelectionFromTop(position, getChildAt(position - getFirstVisiblePosition()).getTop()+scrollHeight);  
  228.                 }  
  229.                   
  230.             }  
  231.               
  232.             break;  
  233.         case MotionEvent.ACTION_UP:  
  234.               
  235.             int position = this.pointToPosition((int)x, (int)y);  
  236.               
  237.             if(position == INVALID_POSITION){  
  238.                 position = startPosition;  
  239.             }  
  240.               
  241.             float listTop = getChildAt(0).getTop();  
  242.               
  243.             //这个是ListView可视区域的最下方,但是不一定数据集中的最后一项的位置  
  244.             float listBottom = getChildAt(getChildCount()-1).getBottom();  
  245.               
  246.             if(y<listTop){  
  247.                   
  248.                 position = 0;  
  249.                   
  250.             }else if(y > listBottom){  
  251.                 /** 
  252.                  * 因为向下拖动的时候,ListView是向下滑动的 
  253.                  * 这里当item被往下拖到最下面时,将其添加到数据集中最后一个位置 
  254.                  * 这里注意是getCount()而不是getCount()-1。是向最后插入一个 
  255.                  */  
  256.                 position = getAdapter().getCount();  
  257.             }  
  258.               
  259.             stopDrag(position);  
  260.             break;  
  261.         }  
  262.           
  263.         return true;  
  264.     }  
  265.   
  266.     private void stopDrag(int newPosition) {  
  267.         mDragging = false;  
  268.         if(mDragBitmap != null){  
  269.             mDragBitmap.recycle();  
  270.         }  
  271.           
  272.         //将拖动的item加入新的位置   
  273.         addItem(newPosition);  
  274.   
  275.         invalidate();  
  276.     }  
  277.       
  278.     /** 
  279.      * 用当前View的绘制缓冲区创建一个Bitmap 
  280.      * 同时进行一定的缩放 
  281.      * @param view 
  282.      * @return 
  283.      */  
  284.     private Bitmap createViewBitmap(View view) {  
  285.           
  286.         final int left = view.getLeft();  
  287.         final int top = view.getTop();  
  288.           
  289.         //当前按住的位置相对于View本身的偏移量  
  290.         mTouchOffsetX = mLastMotionX - left;  
  291.         mTouchOffsetY = mLastMotionY - top;  
  292.           
  293.         view.clearFocus();  
  294.         view.setPressed(false);  
  295.           
  296.         boolean willNotCache = view.willNotCacheDrawing();  
  297.         view.setWillNotCacheDrawing(false);  
  298.         view.buildDrawingCache();  
  299.           
  300.         Bitmap bitmap = view.getDrawingCache();  
  301.         final int width = bitmap.getWidth();  
  302.         final int height = bitmap.getHeight();  
  303.           
  304.         //使用一个简单的Scale缩放   
  305.         Matrix matrix = new Matrix();  
  306.         float scaleFactor = view.getHeight();  
  307.         scaleFactor = (scaleFactor+40)/scaleFactor;  
  308.         matrix.setScale(1.0f, scaleFactor);  
  309.           
  310.         Bitmap newBitmap = Bitmap.createBitmap(bitmap, 00, width, height, matrix, true);  
  311.           
  312.         view.destroyDrawingCache();  
  313.         view.setWillNotCacheDrawing(willNotCache);  
  314.           
  315.         //创建的缩放后的Bitmap和原Bitmap的padding  
  316.         mBitmapOffsetX = (newBitmap.getWidth()-width)/2;  
  317.         mBitmapOffsetY = (newBitmap.getHeight()-height)/2;  
  318.           
  319.         return newBitmap;  
  320.     }  
  321.   
  322. }  


0 0
原创粉丝点击