Android滑动删除控件

效果展示

代码实现

静态布局

自定义一个ViewGroup,继承至FrameLayout,覆写其中的几个关键方法,用于给其中的两个子view设置布局位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
private View contentView, deleteView;
int contentViewHeight, contentViewWidth;
int deleteViewHeight, deleteViewWidth;
private void init() {
}
/**
* 从xml中加载完布局,只知道有几个子view,并没有进行测量
* 一般可以初始化子view的引用
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
contentView = getChildAt(0);
deleteView = getChildAt(1);
}
/**
* 测量完子view后调用,在这里可以直接获取子view的高度
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
contentViewWidth = contentView.getMeasuredWidth();
deleteViewHeight = deleteView.getMeasuredHeight();
}
/**
* 放置子view到合适的位置
*/
@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, contentViewWidth, contentViewHeight);
deleteView.layout(contentViewWidth, 0, contentViewWidth + deleteViewWidth, deleteViewHeight);
}
```
### 处理滑动逻辑
利用ViewDragHelper类封装了对触摸位置、速度、距离的检测,以及Scroller.
需要我们制定什么时候滑动,以及滑动多少。
需要把ViewGroup中受到的触摸事件传给ViewDragHelper实例。
#### 触摸事件传给ViewDragHelper实例
```java
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
//消费掉此触摸事件,不向上返回
return true;
}

####在ViewDragHelper的回调函数中处理滑动逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
private ViewDragHelper mViewDragHelper;
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* @return 返回true表示获得view的控制权
*/
@Override
public boolean tryCaptureView(View view, int i) {
return view == contentView || view == deleteView;
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
/**
* 控制view在水平方向上实际滑动了多少
* @param child 当前触摸的view
* @param left view的左边坐标,负数表示view的左边超出父view边界的长度
* @param dx
* @return 返回多少,代表想让child的left=多少
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.i(TAG, "clampViewPositionHorizontal-->" + "left=" + left);
if (child == contentView) {
if (left > 0) left = 0;
if (left < -deleteViewWidth) left = -deleteViewWidth;
} else if (child == deleteView) {
//使deleteVie不会超出指定的边界
if (left < contentViewWidth - deleteViewWidth) {
left = contentViewWidth - deleteViewWidth;
}
}
return left;
}
/**
* 水平方向拖拽的范围
*/
@Override
public int getViewHorizontalDragRange(View child) {
return super.getViewHorizontalDragRange(child);
}
/**
* view滑动后的回调
* @param changedView
* @param left
* @param top
* @param dx x轴方向的改编值
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
Log.i(TAG, "onViewPositionChanged-->" + "dx=" + dx);
//重新布局子view的位置
if (changedView == contentView) {
deleteView.layout(deleteView.getLeft() + dx, 0, deleteView.getRight() + dx, deleteView.getBottom());
} else if (changedView == deleteView) {
contentView.layout(contentView.getLeft() + dx, 0, contentView.getRight() + dx, contentView.getBottom());
}
}
/**
* TouchUp的回调
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (contentView.getLeft() < -deleteViewWidth / 2) { //滑动条打开状态
mViewDragHelper.smoothSlideViewTo(contentView, -deleteViewWidth, 0);
} else { //滑动条关闭状态
mViewDragHelper.smoothSlideViewTo(contentView, 0, 0);
}
//
ViewCompat.postInvalidateOnAnimation(SwipeView.this); //动画刷新
}
};

动画效果

Scroller帮助计算好view在某个时间点会处于某个位置,达到动画的效果

1
2
3
4
5
6
7
8
9
@Override
public void computeScroll() {
super.computeScroll();
if(mViewDragHelper.continueSettling(true)){ //内部有Scroller计算位置和移动
ViewCompat.postInvalidateOnAnimation(SwipeView.this); //刷新当前view
}
}

ListView中的滑动删除效果

完整代码

github