您的位置:首页 > 移动开发 > Android开发

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

2013-04-22 21:18 375 查看
当前是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 implements
OnTouchListener {

private GestureDetector mDetector;
private DragSortListView mListView;

public DragSortController(DragSortListView listView) {
mListView = listView;
mDetector = new GestureDetector(this);
}

@Override
public boolean onTouch(View v, MotionEvent event) {

mDetector.onTouchEvent(event);

// 永远返回false,当前只是辅助手势处理
// 不消耗Touch Event
return false;
}

}


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

@Override
public 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;
}

@Override
public void onLongPress(MotionEvent e) {
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}

@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}

@Override
public 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) {
// 获取当前拖动Item
View 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) {
// 隐藏拖动Item
srcItem.setVisibility(View.INVISIBLE);
}

// 重新请求布局,使以上设置生效,最终会触发dispatchDraw
requestLayout();

return true;
}


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

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

5.  显示浮动视图

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (mFloatView != null) {
if (mFloatView.isLayoutRequested()) {
measureFloatView();
}
mFloatViewOnMeasured = true;
}

mWidthMeasureSpec = widthMeasureSpec;
}

@Override
protected void layoutChildren() {
super.layoutChildren();

if (mFloatView != null) {
if (mFloatView.isLayoutRequested() && !mFloatViewOnMeasured) {
measureFloatView();
}

// 测量宽高后,指定位置
mFloatView.layout(0, 0, mFloatView.getMeasuredWidth(), mFloatView.getMeasuredHeight());
mFloatViewOnMeasured = false;
}

}

@Override
protected 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不能左右移动。

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

// * 保存坐标信息
saveTouchCoords(ev);

return super.onInterceptTouchEvent(ev);
}

@Override
public 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐