前言:关于滑动删除的内容相信大家都在QQ上使用过,滑动删除的出现主要替换了长按item弹出的菜单,这两个方法各有优点,QQ使用的是滑动删除,则微信使用的是长按item选项,各位看官自己酌情使用,实现起来这个效果方法也很多,可以借助Scroller帮助类,也可以使用ViewDragHelper类,本篇博客讲述借助ViewDragHelper类来实现滑动选择菜单。
-----------------------分割线----------------------
ViewDragHelper类是滑动帮助类,v4包下的类,难点是理解ViewDragHelper.CallBack的相关方法,(Hongyang讲解的《Android ViewDragHelper完全解析 自定义ViewGroup神器》大家可以参考下)下面是我总结的介绍CallBack里面的方法:
一:创建实例:
viewDragHelper = ViewDragHelper.create(this, callback);
二:触摸相关方法:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { return viewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { viewDragHelper.processTouchEvent(event); return true; }
三:CallBack里面的方法
1.public boolean tryCaptureView(View child, int pointerId):用于判断是否捕获当前child的触摸事件,child: 当前触摸的子View , return: true:就捕获并解析 false:不处理。
2.public int getViewHorizontalDragRange(View child):获取view水平方向的拖拽范围,但是目前不能限制边界,返回的值目前用在手指抬起的时候view缓慢移动的动画世界的计算上面,切记最好不要返回0。
3.public int clampViewPositionHorizontal(View child, int left, int dx):left表示控制child在水平方向的移动,dx表示ViewDragHelper认为你想让当前child的left改变的值,return 表示你真正想让child的left变成的值
4.public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy):top表示控制child在垂直方向的移动,dx表示ViewDragHelper认为你想让当前child的top改变的值
5.public void onViewReleased(View releasedChild, float xvel, float yvel):当child的位置改变的时候执行,一般用来做其他子View的伴随移动
------------------------分割线--------------------------
实现滑动选择菜单逻辑:
1.自定义一个可以滑动的布局(这个我们可以借助ViewDragHelper)。
2.将该布局放入adapter的布局中,需要处理滑动冲突。
----------------------------分割线------------------------
ok,我们先来实现滑动的布局。
1.首先初始化ViewDragHelper类,这点毋容置疑。
2.先在onFinishInflate里面获取contentView和deleteView。
@Override
protected void onFinishInflate() {
super.onFinishInflate();
contentView = getChildAt(0);
deleteView = getChildAt(1);
}
3.然后在onSizeChanged里面获取宽高属性。(在这里强调一点由于我们继承的是FrameLayout所以不需要重写onMeasure方法来获取宽高)
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
deleteHeight = deleteView.getMeasuredHeight();
deleteWidth = deleteView.getMeasuredWidth();
contentWidth = contentView.getMeasuredWidth();
}
4.在onLayout里面摆放deleteView和contentView的位置。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// super.onLayout(changed, left, top, right, bottom);
contentView.layout(0, 0, contentWidth, deleteHeight);
deleteView.layout(contentView.getRight(), 0, contentView.getRight()
+ deleteWidth, deleteHeight);
}
5.然后在重写触摸相关的方法。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
//如果当前有打开的,则需要直接拦截,交给onTouch处理
if (!SwipeLayoutManager.getInstance().isShouldSwipe(this)) {
//先关闭已经打开的 layoutSwipeLayoutManager.getInstance().closeCurrentLayout();
result = true;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//如果当前有打开的,则下面的逻辑不能执行
if (!SwipeLayoutManager.getInstance().isShouldSwipe(this)) {
requestDisallowInterceptTouchEvent(true);
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
//1.获取x和y方向移动的距离
float moveX = event.getX();
float moveY = event.getY();
float delatX = moveX - downX;//x方向移动的距离
float delatY = moveY - downY;//y方向移动的距离
if (Math.abs(delatX) > Math.abs(delatY)) {
//表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求listview不要拦截
requestDisallowInterceptTouchEvent(true);
}
//更新downX,downY
downX = moveX;
downY = moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
viewDragHelper.processTouchEvent(event);
return true;
}
6.实现CallBack里面的方法:
private ViewDragHelper.Callback callback = new Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == contentView || child == deleteView;
}
@Override
public int getViewHorizontalDragRange(View child) {
return deleteWidth;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == contentView) {
if (left > 0) left = 0;
if (left < -deleteWidth) left = -deleteWidth;
} else if (child == deleteView) {
if (left > contentWidth) left = contentWidth;
if (left < (contentWidth - deleteWidth)) left = contentWidth - deleteWidth;
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == contentView) {
//手动移动deleteView
deleteView.layout(deleteView.getLeft() + dx, deleteView.getTop() + dy,
deleteView.getRight() + dx, deleteView.getBottom() + dy);
} else if (deleteView == changedView) {
//手动移动contentView
contentView.layout(contentView.getLeft() + dx, contentView.getTop() + dy,
contentView.getRight() + dx, contentView.getBottom() + dy);
}
//判断开和关闭的逻辑
if (contentView.getLeft() == 0 && currentState != SwipeState.Close) {
//说明应该将state更改为关闭
currentState = SwipeState.Close;
//回调接口关闭的方法
if (listener != null) {
listener.onClose(getTag());
}
//说明当前的SwipeLayout已经关闭,需要让Manager清空一下
SwipeLayoutManager.getInstance().clearCurrentLayout();
} else if (contentView.getLeft() == -deleteWidth && currentState != SwipeState.Open) {
//说明应该将state更改为开
currentState = SwipeState.Open;
//回调接口打开的方法
if (listener != null) {
listener.onOpen(getTag());
}
//当前的Swipelayout已经打开,需要让Manager记录一下下
SwipeLayoutManager.getInstance().setSwipeLayout(SwipeLayout.this);
}
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (contentView.getLeft() < -deleteWidth / 2) {
//应该打开
open();
} else {
//应该关闭
close();
}
}
};
/**
* 打开的方法
*/
public void open() {
viewDragHelper.smoothSlideViewTo(contentView, -deleteWidth, contentView.getTop());
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
/**
* 关闭的方法
*/
public void close() {
viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
;
public void computeScroll() {
if (viewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
-----------------------分割线---------------------
完成以上代码可以滑动View,在listView中可以滑动多个item,这个肯定不是我们希望看到的,所以我们需要管理一下这个SwipeLayout。
逻辑:1.记录打开的SwipeLayout
2.当钱打开的SwipeLayout关闭的时候,需要清空记录。
3.判断当前是否应该能够滑动,如果没有打开的,则可以滑动,如果有打开的,则判断打开的layout是否和当前按下的layout是否是同一个。
以下是管理类的完整代码:
public class SwipeLayoutManager {
private SwipeLayoutManager(){}
private static SwipeLayoutManager mInstance = new SwipeLayoutManager();
public static SwipeLayoutManager getInstance(){
return mInstance;
}
private SwipeLayout currentLayout;//用来记录当前打开的SwipeLayout
public void setSwipeLayout(SwipeLayout layout){
this.currentLayout = layout;
}
/**
* 清空当前所记录的已经打开的layout
*/
public void clearCurrentLayout(){
currentLayout = null;
}
/**
* 关闭当前已经打开的SwipeLayout
*/
public void closeCurrentLayout(){
if(currentLayout!=null){
currentLayout.close();
}
}
/**
* 判断当前是否应该能够滑动,如果没有打开的,则可以滑动。
* 如果有打开的,则判断打开的layout和当前按下的layout是否是同一个
*
* @return
*/
public boolean isShouldSwipe(SwipeLayout swipeLayout){
if(currentLayout==null){
//说明当前木有打开的layout
return true;
}else {
//说明有打开的layout
return currentLayout==swipeLayout;
}
}
}
---------------------------完整代码下载-----------------------------------
下载:Android开发之滑动选择菜单(仿QQ滑动删除)代码
-------------------欢迎关注我的博客,并给我积极留言--------------