ViewDragHelper介绍

转载请注明出处,有问题的话欢迎在下面留言,我会一一回复,谢谢大家。你们的关注是我最大的动力。

在自定义控件中,很多效果都需要重写onInterceptTouchEvent和onTouchEvent这两个类,但要考虑到各种情况,写出很好的手势处理控件是比较麻烦的。比如多点触控,速度监测等。ViewDragHelper是谷歌io大会上隆重推出的手势处理的类。在android.support.v4中。像Google官方侧滑菜单,DrawerLayout就是用这个类实现的。本节先介绍它的基本用法,以后会介绍如何用ViewDragHelper自定义侧滑菜单。

基本实现

创建实例

mViewDragHelper = ViewDragHelper.create(this,1.0f,callback);

需要传入三个参数,第一个参数传入当前的ViewGroup;第二个参数是灵敏度,数值越大,灵敏度越低;第三个参数传入ViewDragHelper.Callback,后面详细介绍。

触摸相关的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 让ViewDragHelper帮我们判断是否应该拦截
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 将触摸事件交给ViewDragHelper来解析处理
viewDragHelper.processTouchEvent(event);
return true;
}

ViewDragHelper.Callback的相关方法

callback的方法,大家参考注释。

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* 用于判断是否捕获当前child的触摸事件 child: 当前触摸的子View return: true:就捕获并解析 false:不处理
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == blueView || child == redView;
}
/**
* 当view被开始捕获和解析的回调 capturedChild:当前被捕获的子view
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
// Log.e("tag", "onViewCaptured");
}
/**
* 获取view水平方向的拖拽范围,但是目前不能限制边界,返回的值目前用在手指抬起的时候view缓慢移动的动画世界的计算上面; 最好不要返回0
*/
@Override
public int getViewHorizontalDragRange(View child) {
return getMeasuredWidth() - child.getMeasuredWidth();
}
/**
* 获取view垂直方向的拖拽范围,最好不要返回0
*/
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
/**
* 控制child在水平方向的移动 left:
* 表示ViewDragHelper认为你想让当前child的left改变的值,left=chile.getLeft()+dx dx:
* 本次child水平方向移动的距离 return: 表示你真正想让child的left变成的值
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (left < 0) {
// 限制左边界
left = 0;
} else if (left > (getMeasuredWidth() - child.getMeasuredWidth())) {
// 限制右边界
left = getMeasuredWidth() - child.getMeasuredWidth();
}
return left;
}
/**
* 控制child在垂直方向的移动 top:
* 表示ViewDragHelper认为你想让当前child的top改变的值,top=chile.getTop()+dy dy:
* 本次child垂直方向移动的距离 return: 表示你真正想让child的top变成的值
*/
public int clampViewPositionVertical(View child, int top, int dy) {
if (top < 0) {
top = 0;
} else if (top > getMeasuredHeight() - child.getMeasuredHeight()) {
top = getMeasuredHeight() - child.getMeasuredHeight();
}
return top;
};
/**
* 当child的位置改变的时候执行,一般用来做其他子View的伴随移动 changedView:位置改变的child
* left:child当前最新的left top: child当前最新的top dx: 本次水平移动的距离 dy: 本次垂直移动的距离
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == blueView) {
// blueView移动的时候需要让redView跟随移动
redView.layout(redView.getLeft() + dx, redView.getTop() + dy,
redView.getRight() + dx, redView.getBottom() + dy);
} else if (changedView == redView) {
// redView移动的时候需要让blueView跟随移动
blueView.layout(blueView.getLeft() + dx,
blueView.getTop() + dy, blueView.getRight() + dx,
blueView.getBottom() + dy);
}
//1.计算view移动的百分比
float fraction = changedView.getLeft()*1f/(getMeasuredWidth()-changedView.getMeasuredWidth());
Log.e("tag", "fraction:"+fraction);
//2.执行一系列的伴随动画
executeAnim(fraction);
}
/**
* 手指抬起的执行该方法, releasedChild:当前抬起的view xvel: x方向的移动的速度 正:向右移动, 负:向左移动
* yvel: y方向移动的速度
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
int centerLeft = getMeasuredWidth() / 2
- releasedChild.getMeasuredWidth() / 2;
if (releasedChild.getLeft() < centerLeft) {
// 在左半边,应该向左缓慢移动
viewDragHelper.smoothSlideViewTo(releasedChild, 0,
releasedChild.getTop());
ViewCompat.postInvalidateOnAnimation(DragLayout.this);
} else {
// 在右半边,应该向右缓慢移动
viewDragHelper.smoothSlideViewTo(releasedChild,
getMeasuredWidth() - releasedChild.getMeasuredWidth(),
releasedChild.getTop());
ViewCompat.postInvalidateOnAnimation(DragLayout.this);
}
}
};
/**
* 执行伴随动画
* @param fraction
*/
private void executeAnim(float fraction){
//fraction: 0 - 1
//缩放
// ViewHelper.setScaleX(redView, 1+0.5f*fraction);
// ViewHelper.setScaleY(redView, 1+0.5f*fraction);
//旋转
// ViewHelper.setRotation(redView,360*fraction);//围绕z轴转
ViewHelper.setRotationX(redView,360*fraction);//围绕x轴转
// ViewHelper.setRotationY(redView,360*fraction);//围绕y轴转
ViewHelper.setRotationX(blueView,360*fraction);//围绕z轴转
//平移
// ViewHelper.setTranslationX(redView,80*fraction);
//透明
// ViewHelper.setAlpha(redView, 1-fraction);
//设置过度颜色的渐变
redView.setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.GREEN));
// setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.GREEN));
}
public void computeScroll() {
if (viewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(DragLayout.this);
}
}

其他代码

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
/**
* 当DragLayout的xml布局的结束标签被读取完成会执行该方法,此时会知道自己有几个子View了 一般用来初始化子View的引用
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//要测量我自己的子View
// int size = getResources().getDimension(R.dimen.width);//100dp
// int measureSpec =
MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width,MeasureSpec.EXACTLY);
// redView.measure(measureSpec,measureSpec);
// blueView.measure(measureSpec, measureSpec);
//如果说没有特殊的对子View的测量需求,可以用如下方法
measureChild(redView, widthMeasureSpec, heightMeasureSpec);
measureChild(blueView, widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingLeft();
int top = getPaddingTop();
redView.layout(left, top, left + redView.getMeasuredWidth(), top
+ redView.getMeasuredHeight());
blueView.layout(left, redView.getBottom(),
left + blueView.getMeasuredWidth(), redView.getBottom()
+ blueView.getMeasuredHeight());
}

布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<xyz.ibat.circleview.test.DragLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="@dimen/width"
android:layout_height="@dimen/width"
android:background="#ff0000" />
<TextView
android:layout_width="@dimen/width"
android:layout_height="@dimen/width"
android:background="#0000ff" />
</xyz.ibat.circleview.test.DragLayout>

OK~~源码里注释写的很详细,就不过多描述了。开始打造属于你的ViewGroup吧!