今天带大家实现简书的滑动返回效果.
先看看效果图:
最终效果图.gif因为没有具体内容,也没有简书的图片资源,所以稍微简陋了点.但是依然不妨碍我们的效果展示~
OK,接下来惯例,通过阅读本文你能学习到:
- ViewDragHelper的使用(如果你想学习自定义View,那么ViewDragHelper你绝对不能错过)
- 好像也没有什么了....
这个效果,难度不大,会ViewDragHelper的同学应该10分钟就能写出来了吧~如果不会也没关系~
1. 我们自定义一个SwipeBackFrameLayout继承自FrameLayout1.1 因为看到左边黄色的View是被遮住的,而另外一个View的宽度是MatchParent的,所以FrameLayout是不错的选择.顺便增加一个回调,通知activity去finish
1
2
3
4
5
6
7
|
public
void
setCallback(Callback mCallback){
this
.mCallback = mCallback;
}
private
Callback mCallback;
public
interface
Callback{
void
onShouldFinish();
}
|
1.2 Xml布局,非常简单:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<
yifeiyuan.practice.practicedemos.drager.SwipeBackFrameLayout
android:id
=
"@+id/swipe_back"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
"yifeiyuan.practice.practicedemos.drager.SwipeBackActivity"
>
<
TextView
android:layout_width
=
"40dp"
android:layout_height
=
"match_parent"
android:text
=
"@string/hello_world"
android:gravity
=
"center"
android:background
=
"#ffff00"
/>
<
View
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#ff00ff"
/>
</
yifeiyuan.practice.practicedemos.drager.SwipeBackFrameLayout
>
|
1.3 实例化一个ViewDragHelper
1
2
3
4
5
6
7
8
9
|
//1f代表灵敏度
mDragHelper = ViewDragHelper.create(
this
, 1f,
new
ViewDragHelper.Callback() {
@Override
public
boolean
tryCaptureView(View child,
int
pointerId) {
return
false
;
}
}
//因为我们是从左向右滑动 所以设置EDGE_LEFT
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
|
1.4 在SwipeBackFrameLayout里实例化xml里的子View
1
2
3
4
5
6
7
8
9
|
private
View mDividerView;
private
View mContentView;
@Override
protected
void
onFinishInflate() {
super
.onFinishInflate();
mDividerView = getChildAt(
0
);
mDividerView.setAlpha(0f);
mContentView = getChildAt(
1
);
}
|
1.5 让ViewDragHelper处理touch事件
1
2
3
4
5
6
7
8
9
|
@Override
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
return
mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return
true
;
}
|
1.6重写ViewDragHelper的一些处理方法
已附上详细注释
01
02
03
04
05
06
07
08
09
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
|
@Override
public
void
onEdgeTouched(
int
edgeFlags,
int
pointerId) {
super
.onEdgeTouched(edgeFlags, pointerId);
//触摸到左边界的时候 我们capture住mContentView
mDragHelper.captureChildView(mContentView, pointerId);
}
@Override
public
int
getViewHorizontalDragRange(View child) {
return
1
;
}
[url=home.php?mod=space&uid=
389554
]
@Override
[/url]
public
void
onViewPositionChanged(View changedView,
int
left,
int
top,
int
dx,
int
dy) {
super
.onViewPositionChanged(changedView, left, top, dx, dy);
Log.d(TAG,
"onViewPositionChanged() called with left = ["
+ left +
"], top = ["
+ top +
"], dx = ["
+ dx +
"], dy = ["
+ dy +
"]"
);
//0.0 - 1.0
//Notice 这边可以给个接口回调出去,就可以做各种炫酷的效果了
float
alpha = (
float
) (left*
1.0
/mDividerWidth);
mDividerView.setAlpha(alpha);
}
@Override
public
int
clampViewPositionHorizontal(View child,
int
left,
int
dx) {
// Log.d(TAG, "clampViewPositionHorizontal() called with dx = [" + dx + "]");
// 计算left 我们的目标范围是0-dividerwidth的宽度
mLastdx = dx;
int
newLeft = Math.min(mDividerWidth, Math.max(left,
0
));
return
newLeft;
}
@Override
public
void
onViewReleased(View releasedChild,
float
xvel,
float
yvel) {
//>0代表用户想关闭
if
(mLastdx>
0
){
// 还不到关闭条件,我们让view滑动过去,再关闭
if
(mDividerWidth != releasedChild.getLeft()) {
mDragHelper.settleCapturedViewAt(mDividerWidth,releasedChild.getTop();
invalidate();
}
else
{
if
(mCallback !=
null
) {
mCallback.onShouldFinish();
}
}
}
else
{
//用户不想关闭 ,则滑动到最左边
if
(mDividerWidth !=
0
) {
mDragHelper.settleCapturedViewAt(
0
, releasedChild.getTop());
invalidate();
}
}
}
@Override
public
void
onViewDragStateChanged(
int
state) {
super
.onViewDragStateChanged(state);
//滑动停止,并且到达了滑动的判断条件 则回调关闭
if
(mDragHelper.getViewDragState()==ViewDragHelper.STATE_IDLE&&mCallback !=
null
&&mDividerWidth==mContentView.getLeft()&&mLastdx>
0
) {
mCallback.onShouldFinish();
}
}
});
|
1.7 增加对view滑动事件处理,对于以上mDividerWidth我们在onLayout里获取
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private
int
mDividerWidth;
@Override
protected
void
onLayout(
boolean
changed,
int
left,
int
top,
int
right,
int
bottom) {
super
.onLayout(changed, left, top, right, bottom);
mDividerWidth = mDividerView.getWidth();
}
//Notice view 刚初始化的时候就会被调用一次
@Override
public
void
computeScroll() {
super
.computeScroll();
// Log.d(TAG, "computeScroll() called with " + "");
if
(mDragHelper.continueSettling(
true
)) {
invalidate();
}
}
|
我们写完自定义view后还需要自定义一下activity的退出动画~
2.定义activity的finish动画2.1 在anim目录下,创建两个动画xml:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
//no_anim
<
alpha
android:duration
=
"300"
android:fromAlpha
=
"1.0"
android:toAlpha
=
"1.0"
></
alpha
>
//out_to_right
<
translate
android:duration
=
"300"
android:fromXDelta
=
"0%"
android:toXDelta
=
"100%"
></
translate
>
|
2.2 在activity里设置callback监听,并运用动画
1
2
3
4
5
6
7
|
mSwipeBack.setCallback(
new
SwipeBackFrameLayout.Callback() {
@Override
public
void
onShouldFinish() {
finish();
overridePendingTransition(R.anim.no_anim, R.anim.out_to_right);
}
});
|
好了!!~代码量非常少!~就是这么简单~
吐槽一下,简书对代码块的支持太差了,代码复制过来全是乱的!!
同学们还是去看源码吧:
源码在我的 Github (https://github.com/AlanCheen/PracticeDemo)上
如果你觉得喜欢,举手之劳,给我点个赞吧!~
如果有什么不好的,不对的欢迎指出!~
如果有什么更好的方式,也欢迎指导!!!!
下次见!~