Android ListView 可拖拽Item 1 - 创建浮动Item

来源:互联网 发布:影视美术设计考研知乎 编辑:程序博客网 时间:2024/06/10 03:33


当前是ListView 可拖拽Item第一步,显示浮动视图。看看下面的图片演示浮动视图,但是也可以看到浮动视图之外的Item不会自动移动,并且浮动视图还可以左右移动等问题存在,所以这是第一部分,也是这个效果的起步。


一、有图有真相



二、分析

    

    1. 效果布局,因为是控制ListView子视图Item,而要控制的Item的父类ListView并没有提供可直接控制Item的方法和其他方式,所以这里选择自定义ListView来进行扩展。

    2.  首先是浮动视图的实现,当前是使用在ListView中隐藏选中的Item,并绘制出选中Item视图。具体实现分析请参考《Android 浮动视图效果 第三种实现方式》

         这种浮动视图的实现我也是第一次见,之前都没想象过还能这样实现浮动视图,把视图转换为图片,然后把图片绘制出来实现浮动效果。

    3.  ListView 可拖拽Item中会使用很多手势,这些手势可以通过自己监听判断来得到,但是也可以使用系统提供的手势辅助类实现。具体实现可以参考

          《Android 手势识别,使用SimpleOnGestureListener,OnGestureListener,OnDoubleTapListener》


三、伪代码

1. 首先自定义ListView\

public class DragSortListView extends ListView {public DragSortListView(Context context) {super(context);initCustomListView();}public DragSortListView(Context context, AttributeSet attrs) {super(context, attrs);initCustomListView();}public DragSortListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initCustomListView();}private void initCustomListView() {// setOnTouchListener 与 当前视图onTouchEvent回到不冲突// 前者是在dispatchTouchEvent中调用,后者是在其后执行DragSortController controller = new DragSortController(this);setOnTouchListener(controller);}}


2.   自定义ListView构造参数中添加了Touch Event 事件监听,用于把手势相关的代码摘出来,不用都放到自定义ListView代码也更清晰,下面再来看看自定义手势类中都做了些什么? 先来看看自定义手势:

public class DragSortController extends SimpleOnGestureListener implementsOnTouchListener {private GestureDetector mDetector;private DragSortListView mListView;public DragSortController(DragSortListView listView) {mListView = listView;mDetector = new GestureDetector(this);}@Overridepublic boolean onTouch(View v, MotionEvent event) {mDetector.onTouchEvent(event);// 永远返回false,当前只是辅助手势处理// 不消耗Touch Eventreturn false;}}


3. 自定义手势中onDown事件中触发ListView Item拖动,用于获取当前拖动Item位置,手指在Item中离当前Item左侧与顶部的偏移量。

@Overridepublic boolean onDown(MotionEvent event) {mHitPos = startDragPosition(event);if (mHitPos != MISS) {int deltaX = (int) event.getX() - mItemX;int deltaY = (int) event.getY() - mItemY;startDragPosition(mHitPos, deltaX, deltaY);}return true;}@Overridepublic void onLongPress(MotionEvent e) {}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {return false;}@Overridepublic boolean onSingleTapUp(MotionEvent e) {return true;}@Overridepublic void onShowPress(MotionEvent e) {}// ===========================================================// Private Methods// ===========================================================/** * 在ACTION_DOWN获取开始拖动时的Item位置 *  * @param event  ACTION_DOWN MotionEvent对象 * @return  */public int startDragPosition(MotionEvent event) {return dragHandleHitPosition(event);}/** * 检测Item是否可拖拽,如果可以返回拖动的Item位置 *  * @param event * @return  拖动的Item位置 */private int dragHandleHitPosition(MotionEvent event) {return viewIdHitPosition(event, 0);}/** * 获取拖拽Item位置 *  * @param event  * @param id    可移动区域视图ID,如果整个Item拖动,传入0 * @return      拖动的Item位置 */public int viewIdHitPosition(MotionEvent event, int id) {final int x = (int) event.getX();final int y = (int) event.getY();// 当前选中项int touchPos = mListView.pointToPosition(x, y);// 判断是否在ListView上final int numHeaders = mListView.getHeaderViewsCount();final int numFooters = mListView.getFooterViewsCount();final int count = mListView.getCount();// 仅对Header与Footer之间的Item进行拖动处理if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders&& touchPos < (count - numFooters)) {final View item = mListView.getChildAt(touchPos - mListView.getFirstVisiblePosition());final int rawX = (int) event.getRawX();final int rawY = (int) event.getRawY();// 获取拖动区域视图,如果没有则Item真个视图为可拖动区域View dragBox = id == 0 ? item: item.findViewById(id);if (dragBox != null) {// 选中的是否在拖拽图形内// 获取拖动区域视图,以当期屏幕左上角为原点的相对坐标dragBox.getLocationOnScreen(mTempLoc);if (rawX > mTempLoc[0] && rawY > mTempLoc[1]&& rawX < mTempLoc[0] + dragBox.getWidth()&& rawY < mTempLoc[1] + dragBox.getHeight()) {// 获取与当前Item左上角的距离mItemX = item.getLeft();mItemY = item.getTop();return touchPos;}}}// 没找到拖动视图return MISS;}/** * 启动拖动Item *  * @param position  拖动Item为孩子 * @param deltaX    手指拖动Item距自身左侧偏移量 * @param deltaY    手指拖动Item距自身顶部偏移量 *  * @return 是否可以拖动 */private boolean startDragPosition(int position, int deltaX, int deltaY) {mDraging = mListView.startDrag(position - mListView.getHeaderViewsCount(), deltaX, deltaY);return mDraging;}

4. 创建浮动视图

    上面的3中最后会调用ListView.startDrag,并传递手势类中进行判断收集到了选中的位置,手指在Item中离当前Item左侧与顶部的偏移量。下面来看看ListView.startDrag的具体实现

/** * 开始拖动指定位置Item *  * @param position 拖动Item位置 * @param deltaX   手指拖动Item距自身左侧偏移量 * @param deltaY   手指拖动Item距自身顶部偏移量 * @return 是否可以拖动 */public boolean startDrag(int position, int deltaX, int deltaY) {if (position == MISS) {return false;}// 创建浮动视图ImageView floatView = onCreateFloatView(position);if (floatView == null) {return false;} else {return startDrag(position, floatView, deltaX, deltaY);}}/** * 创建浮动视图 *  * @param position * @return */public ImageView onCreateFloatView(int position) {// 获取当前拖动ItemView srcItem = getChildAt(position + getHeaderViewsCount() - getFirstVisiblePosition());if (srcItem == null) {return null;}// 取消选中状态srcItem.setPressed(false);// 转换为图片srcItem.setDrawingCacheEnabled(true);mSelectedItemBitmap = Bitmap.createBitmap( srcItem.getDrawingCache() );srcItem.setDrawingCacheEnabled(false);// 创建浮动视图if (mFloatView == null) {mFloatView = new ImageView(getContext());}mFloatView.setPadding(0, 0, 0, 0);mFloatView.setImageBitmap(mSelectedItemBitmap);mFloatView.setLayoutParams(new LayoutParams(srcItem.getWidth(), srcItem.getHeight()));return mFloatView;}/** * 处理开始拖动Item *  * @param position * @param floatView  获取的悬浮视图 * @param deltaX * @param deltaY * @return */public boolean startDrag(int position, ImageView floatView, int deltaX, int deltaY) {if (floatView == null) {return false;}// 记录mFloatView = floatView;measureFloatView();// 记录偏移量,供移动时使用mDragDeltaX = deltaX;mDragDeltaY = deltaY;// 更新当前浮动视图位置mFloatLoc.x = mX - mDragDeltaX;mFloatLoc.y = mY - mDragDeltaY;// 当前触摸的Item视图View srcItem = getChildAt(position - getFirstVisiblePosition());if (srcItem != null) {// 隐藏拖动ItemsrcItem.setVisibility(View.INVISIBLE);}// 重新请求布局,使以上设置生效,最终会触发dispatchDrawrequestLayout();return true;}

    先由ListView获取到选中Item的信息,然后传递给自定义ListView,其中把当前Item隐藏掉,隐藏之前把视图转为图片,并设置到ImageView,创建浮动视图重新绘制。

    浮动视图已经创建完成了,如何显示呢? 上面最后执行了requestLayout(), 这个方法会再次请求重新布局,会执行onMeasure,onLayout,onDraw等方法。


5.  显示浮动视图

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (mFloatView != null) {if (mFloatView.isLayoutRequested()) {measureFloatView();}mFloatViewOnMeasured = true;}mWidthMeasureSpec = widthMeasureSpec;}@Overrideprotected void layoutChildren() {super.layoutChildren();if (mFloatView != null) {if (mFloatView.isLayoutRequested() && !mFloatViewOnMeasured) {measureFloatView();}// 测量宽高后,指定位置 mFloatView.layout(0, 0, mFloatView.getMeasuredWidth(), mFloatView.getMeasuredHeight());mFloatViewOnMeasured = false;}}@Overrideprotected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);if (mFloatView != null) {int width = mFloatView.getWidth();int height = mFloatView.getHeight();canvas.save();// 绘制到当前ListView左上角为坐标原点的相应位置canvas.translate(mFloatLoc.x, mFloatLoc.y);canvas.clipRect(0, 0, width, height);canvas.saveLayerAlpha(0, 0, width, height, getAlpha(), canvas.ALL_SAVE_FLAG);// 浮动视图绘制到回调提供的当前Canvas上mFloatView.draw(canvas);canvas.restore();}}
      关于浮动视图的显示与浮动视图的多种实现方式可以查看《Android 浮动视图效果 第三种实现方式》

6. 简单的拖动,最初模型待完善所以只是能拖动,其他任何效果都没有添加比如Item不能左右移动。

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// * 保存坐标信息saveTouchCoords(ev);return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {saveTouchCoords(ev);switch (ev.getAction()) {case MotionEvent.ACTION_MOVE:continueDrag((int)ev.getX(), (int)ev.getY());break;}return super.onTouchEvent(ev);}/** * 记录当前触摸时间参数 *  * @param event */private void saveTouchCoords(MotionEvent event) {mX = (int) event.getX();mY = (int) event.getY();}/** * 控制浮动视图随手指拖动 *  * @param x * @param y */private void continueDrag(int x, int y) {mFloatLoc.x = x - mDragDeltaX;mFloatLoc.y = y - mDragDeltaY;}



四、源码下载   

点击下载源码


转载请注明出处:http://blog.csdn.net/love_world_/article/details/8836449






原创粉丝点击