滚动回弹效果分析:
首先,创建一个类,继承scrollview,重写ontouch事件,实现伸缩回弹效果。
[scroollview节点下只能有一个子节点,这个子节点就是我们要移动的view布局]
第一步:获取要操作的子view布局
第二步:重写onTouch事件监听
分析具体事件:
观察分析得出结论:
让布局移动每一次拉动的Y轴一半的距离,然后松手滚动[携带动画]回到原来的位置。
下拉或者上拉的时候,记录按下时的Y轴位置
action_down:
y
移动过程中的处理:
计算上一次与本次的Y轴(拉动距离)[而不是按下时候的Y值,和现在移动到的Y值,是每上一次和本次的Y值比较
判断是否需要移动布局的情况:Y轴的一个距离偏移
//2种情况,随着布局的拖动, inner.getMeasuredHeight()的值是变化的
//inner.getMeasuredHeight()与getHeight()的区别:
当屏幕可以包裹内容的时候,他们的值相等
当view的高度超出屏幕时,getMeasuredHeight()是实际View的大小,与屏幕无关,getHeight的大小此时则是屏幕的大小。
此时,getMeasuredHeight() = getHeight+超出部分。
抬起的处理:布局回滚到正常位置
移动动画回滚到正常位置(*:动画执行期间,不允许拖拉操作)
距离:-的滚动距离
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
|
public
class
MyScrollview
extends
ScrollView {
//要操作的布局
private
View innerView;
private
float
y;
private
Rect normal =
new
Rect();
private
boolean
animationFinish =
true
;
public
MyScrollview(Context context) {
super
(context,
null
);
}
public
MyScrollview(Context context, AttributeSet attrs) {
super
(context, attrs);
}
public
MyScrollview(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
}
@Override
protected
void
onFinishInflate() {
int
childCount = getChildCount();
if
(childCount >
0
) {
innerView = getChildAt(
0
);
}
}
@Override
public
boolean
onTouchEvent(MotionEvent ev) {
if
(innerView ==
null
) {
return
super
.onTouchEvent(ev);
}
else
{
commonTouchEvent(ev);
}
return
super
.onTouchEvent(ev);
}
/**
* 自定义touch事件处理
*
* @param ev
*/
private
void
commonTouchEvent(MotionEvent ev) {
if
(animationFinish) {
int
action = ev.getAction();
switch
(action) {
case
MotionEvent.ACTION_DOWN:
y = ev.getY();
break
;
case
MotionEvent.ACTION_MOVE:
float
preY = y ==
0
? ev.getY() : y;
float
nowY = ev.getY();
int
detailY = (
int
) (preY - nowY);
y = nowY;
//操作view进行拖动detailY的一半
if
(isNeedMove()) {
//布局改变位置之前,记录一下正常状态的位置
if
(normal.isEmpty()) {
normal.set(innerView.getLeft(), innerView.getTop(), innerView.getRight(), innerView.getBottom());
}
innerView.layout(innerView.getLeft(), innerView.getTop() - detailY /
2
, innerView.getRight(), innerView.getBottom() - detailY /
2
);
}
break
;
case
MotionEvent.ACTION_UP:
y =
0
;
//布局回滚到原来的位置
if
(isNeedAnimation()) {
animation();
}
break
;
}
}
}
private
void
animation() {
TranslateAnimation ta =
new
TranslateAnimation(
0
,
0
,
0
, normal.top - innerView.getTop());
ta.setDuration(
200
);
ta.setAnimationListener(
new
Animation.AnimationListener() {
@Override
public
void
onAnimationStart(Animation animation) {
animationFinish =
false
;
}
@Override
public
void
onAnimationEnd(Animation animation) {
innerView.clearAnimation();
innerView.layout(normal.left, normal.top, normal.right, normal.bottom);
normal.setEmpty();
animationFinish =
true
;
}
@Override
public
void
onAnimationRepeat(Animation animation) {
}
});
innerView.startAnimation(ta);
}
/**
* 判断是否需要回滚
*
* @return
*/
private
boolean
isNeedAnimation() {
return
!normal.isEmpty();
}
/**
* 判断是否需要移动
*
* @return
*/
private
boolean
isNeedMove() {
int
offset = innerView.getMeasuredHeight() - getHeight();
int
scrollY = getScrollY();
Log.e(
"zoubo"
,
"getMeasuredHeight:"
+ innerView.getMeasuredHeight() +
"----getHeight:"
+ getHeight());
Log.e(
"zoubo"
,
"offset:"
+ offset +
"----scrollY:"
+ scrollY);
if
(scrollY ==
0
|| scrollY == offset) {
return
true
;
}
return
false
;
}
}
|