说说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。下面,就不再废话了,直接上代码,具体需要注意的地方请看注释:
- public class DragListView extends ListView{
- private static final String TAG = "DragListView";
- private static final int INVALID_POSITION = -1;
- private Bitmap mDragBitmap;
- private View mDragView;
- private UorderDeleteZone zone;
- private View footer;
- private Object srcContent;
- private int startPosition;
- private Paint mPaint = new Paint();
- private float mLastMotionX;
- private float mLastMotionY;
- private float mTouchOffsetX;
- private float mTouchOffsetY;
- private float mBitmapOffsetX;
- private float mBitmapOffsetY;
- private boolean mDragging = false;
- private Rect mDragRect = new Rect(); //当拖动一个item的时候,记录拖动经过的区域
- //是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。
- private int mScaledTouchSlop;
- private float mTopScrollBound;//向上滑动超过这个边界的时候,上面的item向下滚动
- private float mBottomScrollBound;//向下滑动超过这个边界的时候,下面的item开始向上滚动
- public DragListView(Context context){
- this(context, null);
- }
- public DragListView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public DragListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- /**
- * 当某个item被拖拽时,将其颜色改变下,
- */
- final int srcColor = context.getResources().getColor(R.color.drag_filter);
- mPaint.setColorFilter(new PorterDuffColorFilter(srcColor, PorterDuff.Mode.SRC_ATOP));
- //是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。
- mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- }
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if(mDragging && mDragBitmap != null){
- final int scrollX = getScrollX();
- final int scrollY = getScrollY();
- float left = scrollX + mLastMotionX-mTouchOffsetX-mBitmapOffsetX;
- float top = scrollY + mLastMotionY-mTouchOffsetY-mBitmapOffsetY;
- canvas.drawBitmap(mDragBitmap, left, top, mPaint);
- }
- }
- /**
- * 在这个方法中判断当前用户按下事件所在的位置
- * 如果该位置在右边30dip之内,就进行拖拽
- */
- public boolean onInterceptTouchEvent(MotionEvent event){
- int action = event.getAction();
- final float x = event.getX();
- final float y = event.getY();
- if(getWidth()-x > 30){
- //不是位于拖拽区域,直接返回就OK了
- return super.onInterceptTouchEvent(event);
- }
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionX = x;
- mLastMotionY = y;
- break;
- default:
- break;
- }
- Log.e(TAG, "touchSlop:"+mScaledTouchSlop);
- //计算滚动边界
- /**
- * 这里当向上拖拽到1/3屏幕高度的时候就滚动,但是这里如果开始拖拽的item就是位于这1/3屏幕内位置的
- * 则,取当前小的一个.当然你也就可以设置成1/3的屏幕
- */
- mTopScrollBound = Math.min(y-mScaledTouchSlop, getHeight()/3);
- mBottomScrollBound = Math.min(y + mScaledTouchSlop, getHeight()*2/3);
- // mTopScrollBound = getHeight()/3;
- // mBottomScrollBound = getHeight()*2/3;
- Log.e(TAG, "bound scroll:"+mTopScrollBound+","+mBottomScrollBound);
- //获取当前事件坐标所在的item项,ListView中拖动是上下拖动的,所以,和x坐标关系不大
- int position = this.pointToPosition((int)x, (int)y);
- if(position == INVALID_POSITION){
- return super.onInterceptTouchEvent(event);
- }
- startPosition = position;
- /**
- * 这里获取当前被拖拽的item,注意,因为ListView中并不是每个item都是一个View,这个View是重用的
- */
- mDragView = getChildAt(position-getFirstVisiblePosition());
- srcContent = getItemAtPosition(position);
- if(mDragView != null){
- mDragBitmap = createViewBitmap(mDragView);
- //当item被拖拽后,删除原来位置上的item,其实就是将Adapter中该位置的内容给删掉
- /**
- * 注意,该ListView用的Adapter是ArrayAdapter,其有remove(Object item)方法
- * 但是,经常处理ListView显示数据的应该知道,ArrayList的remove(object)方法实现了,
- * 但是我们使用ArrayAdatper的时候,如果我们的数据传递到Adapter用的是数组,那么ArrayAdapter内
- * 默认使用Arrays.asList(Object[])方法将数组转换为List对象,这样,当我们调用ArrayList的
- * removeItem方法的时候,就会出现java.lang.UnsupportedOperationException异常。这是因为
- * Arrays.asList(Object[])转换后的List是Arrays.ArrayList对象,而不是java.utils.ArrayList
- * Arrays.ArrayList并没有实现remove方法,所以,执行父类AbstractList默认的remove方法,而
- * AbstractList的remove方法就是抛出一个UnsupportedOperationException异常
- */
- removeItem(position);
- mDragging = true;
- }
- return true;
- }
- @SuppressWarnings("unchecked")
- private void removeItem(int position){
- ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();
- adapter.remove(adapter.getItem(position));
- }
- @SuppressWarnings("unchecked")
- private void addItem(int position){
- ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();
- adapter.insert((String)srcContent, position);
- }
- public boolean onTouchEvent(MotionEvent event){
- super.onTouchEvent(event);
- if(!mDragging){
- return false;
- }
- final int action = event.getAction();
- final float x = event.getX();
- final float y = event.getY();
- switch(action){
- case MotionEvent.ACTION_DOWN:
- //mLastMotionX = x;
- mLastMotionY = y;
- break;
- case MotionEvent.ACTION_MOVE:
- final int scrollX = getScrollX();
- final int scrollY = getScrollY();
- int left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);
- int top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);
- final int width = mDragBitmap.getWidth();
- final int height = mDragBitmap.getHeight();
- mDragRect.set(left-1, top-1, width+left+1, top+height+1);
- Log.e(TAG, "每次move的距离:"+(y-mLastMotionY));
- //mLastMotionX = x;
- mLastMotionY = y;
- left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);
- top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);
- mDragRect.union(left-1, top-1, width+left+1, top+height+1);
- invalidate(mDragRect);
- int scrollHeight = 0;
- if(y < mTopScrollBound){
- /**
- * 如果当前move到的y位置为mTopScrollBound之上,则下面获取当前位置的item,
- * 并将该item在当前位置的基础上,向下移动15个dip的偏移量,这样,当向上drag的时候
- * 后面的item向下在滚动
- *
- * 向下拖拽的时候同理
- */
- scrollHeight = 15;
- }else if(y > mBottomScrollBound){
- scrollHeight = -15;
- }
- if(scrollHeight != 0){
- //调用Listview方法实现滚动
- int position = this.pointToPosition((int)x, (int)y);
- if(position != INVALID_POSITION){
- /**
- * 将当前位置的item向上或者向下移动scrollHeight个单位的距离
- */
- setSelectionFromTop(position, getChildAt(position - getFirstVisiblePosition()).getTop()+scrollHeight);
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- int position = this.pointToPosition((int)x, (int)y);
- if(position == INVALID_POSITION){
- position = startPosition;
- }
- float listTop = getChildAt(0).getTop();
- //这个是ListView可视区域的最下方,但是不一定数据集中的最后一项的位置
- float listBottom = getChildAt(getChildCount()-1).getBottom();
- if(y<listTop){
- position = 0;
- }else if(y > listBottom){
- /**
- * 因为向下拖动的时候,ListView是向下滑动的
- * 这里当item被往下拖到最下面时,将其添加到数据集中最后一个位置
- * 这里注意是getCount()而不是getCount()-1。是向最后插入一个
- */
- position = getAdapter().getCount();
- }
- stopDrag(position);
- break;
- }
- return true;
- }
- private void stopDrag(int newPosition) {
- mDragging = false;
- if(mDragBitmap != null){
- mDragBitmap.recycle();
- }
- //将拖动的item加入新的位置
- addItem(newPosition);
- invalidate();
- }
- /**
- * 用当前View的绘制缓冲区创建一个Bitmap
- * 同时进行一定的缩放
- * @param view
- * @return
- */
- private Bitmap createViewBitmap(View view) {
- final int left = view.getLeft();
- final int top = view.getTop();
- //当前按住的位置相对于View本身的偏移量
- mTouchOffsetX = mLastMotionX - left;
- mTouchOffsetY = mLastMotionY - top;
- view.clearFocus();
- view.setPressed(false);
- boolean willNotCache = view.willNotCacheDrawing();
- view.setWillNotCacheDrawing(false);
- view.buildDrawingCache();
- Bitmap bitmap = view.getDrawingCache();
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- //使用一个简单的Scale缩放
- Matrix matrix = new Matrix();
- float scaleFactor = view.getHeight();
- scaleFactor = (scaleFactor+40)/scaleFactor;
- matrix.setScale(1.0f, scaleFactor);
- Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
- view.destroyDrawingCache();
- view.setWillNotCacheDrawing(willNotCache);
- //创建的缩放后的Bitmap和原Bitmap的padding
- mBitmapOffsetX = (newBitmap.getWidth()-width)/2;
- mBitmapOffsetY = (newBitmap.getHeight()-height)/2;
- return newBitmap;
- }
- }
public class DragListView extends ListView{private static final String TAG = "DragListView";private static final int INVALID_POSITION = -1;private Bitmap mDragBitmap;private View mDragView;private UorderDeleteZone zone;private View footer;private Object srcContent;private int startPosition;private Paint mPaint = new Paint();private float mLastMotionX;private float mLastMotionY;private float mTouchOffsetX;private float mTouchOffsetY;private float mBitmapOffsetX;private float mBitmapOffsetY;private boolean mDragging = false;private Rect mDragRect = new Rect(); //当拖动一个item的时候,记录拖动经过的区域//是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。private int mScaledTouchSlop;private float mTopScrollBound;//向上滑动超过这个边界的时候,上面的item向下滚动private float mBottomScrollBound;//向下滑动超过这个边界的时候,下面的item开始向上滚动public DragListView(Context context){this(context, null);}public DragListView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public DragListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);/** * 当某个item被拖拽时,将其颜色改变下, */ final int srcColor = context.getResources().getColor(R.color.drag_filter); mPaint.setColorFilter(new PorterDuffColorFilter(srcColor, PorterDuff.Mode.SRC_ATOP)); //是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。 mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); }protected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);if(mDragging && mDragBitmap != null){final int scrollX = getScrollX();final int scrollY = getScrollY();float left = scrollX + mLastMotionX-mTouchOffsetX-mBitmapOffsetX;float top = scrollY + mLastMotionY-mTouchOffsetY-mBitmapOffsetY;canvas.drawBitmap(mDragBitmap, left, top, mPaint);}}/** * 在这个方法中判断当前用户按下事件所在的位置 * 如果该位置在右边30dip之内,就进行拖拽 */public boolean onInterceptTouchEvent(MotionEvent event){int action = event.getAction();final float x = event.getX();final float y = event.getY();if(getWidth()-x > 30){//不是位于拖拽区域,直接返回就OK了return super.onInterceptTouchEvent(event);}switch (action) {case MotionEvent.ACTION_DOWN:mLastMotionX = x;mLastMotionY = y;break;default:break;}Log.e(TAG, "touchSlop:"+mScaledTouchSlop);//计算滚动边界/** * 这里当向上拖拽到1/3屏幕高度的时候就滚动,但是这里如果开始拖拽的item就是位于这1/3屏幕内位置的 * 则,取当前小的一个.当然你也就可以设置成1/3的屏幕 */mTopScrollBound = Math.min(y-mScaledTouchSlop, getHeight()/3);mBottomScrollBound = Math.min(y + mScaledTouchSlop, getHeight()*2/3);//mTopScrollBound = getHeight()/3;//mBottomScrollBound = getHeight()*2/3;Log.e(TAG, "bound scroll:"+mTopScrollBound+","+mBottomScrollBound);//获取当前事件坐标所在的item项,ListView中拖动是上下拖动的,所以,和x坐标关系不大int position = this.pointToPosition((int)x, (int)y);if(position == INVALID_POSITION){return super.onInterceptTouchEvent(event);}startPosition = position;/** * 这里获取当前被拖拽的item,注意,因为ListView中并不是每个item都是一个View,这个View是重用的 */mDragView = getChildAt(position-getFirstVisiblePosition());srcContent = getItemAtPosition(position);if(mDragView != null){mDragBitmap = createViewBitmap(mDragView);//当item被拖拽后,删除原来位置上的item,其实就是将Adapter中该位置的内容给删掉/** * 注意,该ListView用的Adapter是ArrayAdapter,其有remove(Object item)方法 * 但是,经常处理ListView显示数据的应该知道,ArrayList的remove(object)方法实现了, * 但是我们使用ArrayAdatper的时候,如果我们的数据传递到Adapter用的是数组,那么ArrayAdapter内 * 默认使用Arrays.asList(Object[])方法将数组转换为List对象,这样,当我们调用ArrayList的 * removeItem方法的时候,就会出现java.lang.UnsupportedOperationException异常。这是因为 * Arrays.asList(Object[])转换后的List是Arrays.ArrayList对象,而不是java.utils.ArrayList * Arrays.ArrayList并没有实现remove方法,所以,执行父类AbstractList默认的remove方法,而 * AbstractList的remove方法就是抛出一个UnsupportedOperationException异常 */removeItem(position);mDragging = true;}return true;}@SuppressWarnings("unchecked")private void removeItem(int position){ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();adapter.remove(adapter.getItem(position));}@SuppressWarnings("unchecked")private void addItem(int position){ArrayAdapter<Object> adapter = (ArrayAdapter<Object>)this.getAdapter();adapter.insert((String)srcContent, position);}public boolean onTouchEvent(MotionEvent event){super.onTouchEvent(event);if(!mDragging){return false;}final int action = event.getAction();final float x = event.getX();final float y = event.getY();switch(action){case MotionEvent.ACTION_DOWN://mLastMotionX = x;mLastMotionY = y;break;case MotionEvent.ACTION_MOVE:final int scrollX = getScrollX();final int scrollY = getScrollY();int left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);int top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);final int width = mDragBitmap.getWidth();final int height = mDragBitmap.getHeight();mDragRect.set(left-1, top-1, width+left+1, top+height+1);Log.e(TAG, "每次move的距离:"+(y-mLastMotionY));//mLastMotionX = x;mLastMotionY = y;left = (int)(scrollX + mLastMotionX - mTouchOffsetX - mBitmapOffsetX);top = (int)(scrollY + mLastMotionY - mTouchOffsetY - mBitmapOffsetY);mDragRect.union(left-1, top-1, width+left+1, top+height+1);invalidate(mDragRect);int scrollHeight = 0;if(y < mTopScrollBound){/** * 如果当前move到的y位置为mTopScrollBound之上,则下面获取当前位置的item, * 并将该item在当前位置的基础上,向下移动15个dip的偏移量,这样,当向上drag的时候 * 后面的item向下在滚动 * * 向下拖拽的时候同理 */scrollHeight = 15;}else if(y > mBottomScrollBound){scrollHeight = -15;}if(scrollHeight != 0){//调用Listview方法实现滚动int position = this.pointToPosition((int)x, (int)y);if(position != INVALID_POSITION){/** * 将当前位置的item向上或者向下移动scrollHeight个单位的距离 */setSelectionFromTop(position, getChildAt(position - getFirstVisiblePosition()).getTop()+scrollHeight);}}break;case MotionEvent.ACTION_UP:int position = this.pointToPosition((int)x, (int)y);if(position == INVALID_POSITION){position = startPosition;}float listTop = getChildAt(0).getTop();//这个是ListView可视区域的最下方,但是不一定数据集中的最后一项的位置float listBottom = getChildAt(getChildCount()-1).getBottom();if(y<listTop){position = 0;}else if(y > listBottom){/** * 因为向下拖动的时候,ListView是向下滑动的 * 这里当item被往下拖到最下面时,将其添加到数据集中最后一个位置 * 这里注意是getCount()而不是getCount()-1。是向最后插入一个 */position = getAdapter().getCount();}stopDrag(position);break;}return true;}private void stopDrag(int newPosition) {mDragging = false;if(mDragBitmap != null){mDragBitmap.recycle();}//将拖动的item加入新的位置addItem(newPosition);invalidate();}/** * 用当前View的绘制缓冲区创建一个Bitmap * 同时进行一定的缩放 * @param view * @return */private Bitmap createViewBitmap(View view) {final int left = view.getLeft();final int top = view.getTop();//当前按住的位置相对于View本身的偏移量mTouchOffsetX = mLastMotionX - left;mTouchOffsetY = mLastMotionY - top;view.clearFocus();view.setPressed(false);boolean willNotCache = view.willNotCacheDrawing();view.setWillNotCacheDrawing(false);view.buildDrawingCache();Bitmap bitmap = view.getDrawingCache();final int width = bitmap.getWidth();final int height = bitmap.getHeight();//使用一个简单的Scale缩放Matrix matrix = new Matrix();float scaleFactor = view.getHeight();scaleFactor = (scaleFactor+40)/scaleFactor;matrix.setScale(1.0f, scaleFactor);Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);view.destroyDrawingCache();view.setWillNotCacheDrawing(willNotCache);//创建的缩放后的Bitmap和原Bitmap的paddingmBitmapOffsetX = (newBitmap.getWidth()-width)/2;mBitmapOffsetY = (newBitmap.getHeight()-height)/2;return newBitmap;}}
0 0
- 说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher而实现的附属品(可以拖拽的ListView)
- 说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher而实现的附属品(可以拖拽的ListView)
- 说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher而实现的附属品(可以拖拽的ListView)
- 说说Android桌面(Launcher应用)背后的故事(七)——又是一个附属品(可以转动的绚烂饼图)
- 说说Android桌面(Launcher应用)背后的故事(七)——又是一个附属品(可以转动的绚烂饼图)
- 说说Android桌面(Launcher应用)背后的故事(七)——又是一个附属品(可以转动的绚烂饼图)
- 说说Android桌面(Launcher应用)背后的故事(九)——让我的桌面多姿多彩
- 说说Android桌面(Launcher应用)背后的故事(九)——让我的桌面多姿多彩
- 说说Android桌面(Launcher应用)背后的故事(九)——让我的桌面多姿多彩
- 说说Android桌面(Launcher应用)背后的故事1
- 说说Android桌面(Launcher应用)背后的故事2
- 说说Android桌面(Launcher应用)背后的故事(一)——揭开她神秘的面纱
- 说说Android桌面(Launcher应用)背后的故事(二)——应用程序的添加
- 说说Android桌面(Launcher应用)背后的故事(三)——CellLayout的秘密
- 说说Android桌面(Launcher应用)背后的故事(五)——桌面壁纸的添加
- 说说Android桌面(Launcher应用)背后的故事(二)——应用程序的添加
- 说说Android桌面(Launcher应用)背后的故事(一)——揭开她神秘的面纱
- 说说Android桌面(Launcher应用)背后的故事(一)——揭开她神秘的面纱
- Ubutn操作系统学习笔记三之------文件系统基本结构
- 两个线程A和B,任务都是打印当前时间,要求编码实现:线程A和B同时启动后,以先A后B的方式任务交叉执行10次。
- 说说Android桌面(Launcher应用)背后的故事(五)——桌面壁纸的添加
- 大数据实战
- C++小结(一)—基本了解
- 说说Android桌面(Launcher应用)背后的故事(六)——研究Launcher而实现的附属品(可以拖拽的ListView)
- 哈尔滨理工大学第四届ACM程序设计竞赛J: xiaodao 我爱你!
- 说说Android桌面(Launcher应用)背后的故事(七)——又是一个附属品(可以转动的绚烂饼图)
- 项目管理---敏捷开发思想---带来相当愉快的项目开发过程
- 【FJOI2014】石子合并问题
- Java JDBC 连接Oracle
- 顺序栈
- 类目和延展
- 网络诊断,浏览器不能上网,其他软件都能上网