最近手机升级了5.0系统后,突然间发现微信竟然有IOS一样的侧滑关闭当前页面的效果,就想把这种效果也加进自己的项目里面。本着不重复造轮子的原则,就在网上百度了很久,发现大多数人都是采用自定义View来实现,但是对于我这种已经基本完成的项目来说,如果全部的Activity再重新使用自定义的View无疑是一种可怕的噩梦。
因此,我这里实现了另外一种不需要自定义View也能实现的方法,其子类只要继承于它,便能拥有其侧滑滑动的功能。
随便说一句,此方法仅对5.0以上的手机有效(反正微信也是5.0上才能用),5.0以下的请无视!!!

以上都是废话~~~~
最终效果

原理

在每个Activity里面都有一个底层的View,也就是所谓的rootView,当我们加载一个xml布局时,系统就会自动给你生成这个rootView,由于它是一个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
public class SlideActivity extends AppCompatActivity {
View mRootView;
private GestureDetector mDetector;
private int mWindowWidth;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
mRootView = getWindow().getDecorView();
mRootView.setBackgroundColor(Color.BLUE);
mDetector = new GestureDetector(this, new GestureListener());
mWindowWidth = getWindow().getWindowManager().getDefaultDisplay().getWidth();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event);
}

/**
* 手势监听
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 != null) {
handlerCurrentActivityScroll(e2);
}
return super.onScroll(e1, e2, distanceX, distanceY);
}

/**
* 处理当前页面滑动
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}
}
}

这是我们的效果
初始效果

Activity联动

尼玛,这差距还是很大的,最明显的地方是,我们移动 的时候,上一层Activity竟然没有跟着联动。
解决这个问题的方法也简单,如图所示,每当启动一个Activity时,系统都会把Activity放到一个栈里面,由于栈的工作原理可知,APP里面的Activity是一层覆盖一层的,就如上图所示。为此,每当启动一个Activity时,就可以把当前的Actiivty存储到一个List里面,这样,我们就可以在当前的Activity里面取出上一个Activity进行操作。
因此,在进入一个新的Activity的时候,在其onCreate方法里面把当前的Activity加载到列表里,当退出时,在finish的重载方法里面,将当前Activity从列表里面移除。
注意!!!在滑动的时候必须需要考虑到Activity里面有可能会有类似于ListView一类的滑动控件,因此,我们必须对事件进行分发控制。
代码如下

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
/**
* Created by yuyu on 2015/10/29.
*/
public class TestActivity extends AppCompatActivity {
View mRootView;
private GestureDetector mGestureDetector;
private static List<TestActivity> mActivitys = new ArrayList<>();
/**
* 移动距离
*/
private float mWindowWidth;
private TestActivity mBeforeActivity;
/**
* 上一个Activity偏移量
*/
private float mOffsetX;
/**
* 上一个页面移出的位置
*/
private float mOutsideWidth;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
/**
* 把当前Activity加到列表里面
*/
mActivitys.add(this);
initScrollBack();
}

/**
* 初始化左滑退出功能
*/
private void initScrollBack() {
mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
mOutsideWidth = -mWindowWidth / 4;
mOffsetX = mOutsideWidth;
mGestureDetector = new GestureDetector(this, new GestureListener());
mRootView = getWindow().getDecorView();
mRootView.setBackgroundColor(Color.BLUE);
}

/**
* 控制分发事件,在这里控制能能触发拖动的范围
*/
@Override
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
if (ev.getX() < mWindowWidth / 10) {
if (mActivitys.size() > 1) {
mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
beforeActivityTranslationX(mOutsideWidth);
}
return onTouchEvent(ev);
}
return super.dispatchTouchEvent(ev);
}

@Override
public void finish() {
mActivitys.remove(this);
if (mOffsetX < 0.0001 || mOffsetX > 0.0001) {
beforeActivityTranslationX(0);
}
super.finish();
}

public void onClick(View view) {
Intent intent = new Intent(this, Activity5.class);
startActivity(intent);
}

public View getRootView() {
return mRootView;
}

/**
* 控制上一个Activity移动
*/
private void beforeActivityTranslationX(float translationX) {
if (mBeforeActivity != null) {
mBeforeActivity.getRootView().setTranslationX(translationX);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}

/**
* 手势监听
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 != null) {
handlerCurrentActivityScroll(e2);
handleBeforeActivityScroll(e2, distanceX);
}
return super.onScroll(e1, e2, distanceX, distanceY);
}

/**
* 处理当前页面滑动
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}

/**
* 处理上一个页面滑动
*/
private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
if (mBeforeActivity != null) {
mOffsetX = distanceX < 0 ? mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
if (mOffsetX > 0.0001) {
mOffsetX = 0f;
}
mBeforeActivity.getRootView().setTranslationX(mOffsetX);
}
}
}
}

这是联动后的效果图
联动后的效果
现在算是有点效果了,但是和微信的差距还是很大,接下来我们便需要开始处理自动滑动了

自动滑动

这个就不需要多说了,这个主要就是利用属性动画进行移动
以下是完整的代码

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
public class SlideActivity extends AppCompatActivity {
private static final String TAG = "SlideActivity";
private static List<SlideActivity> mActivitys = new ArrayList<>();
/**
* 手势监听
*/
private GestureDetector mGestureDetector;
private View mRootView;
private boolean isScroll = false;
/**
* 移动距离
*/
private float mWindowWidth;
private SlideActivity mBeforeActivity;
/**
* 上一个Activity偏移量
*/
private float mOffsetX;
/**
* 上一个页面移出的位置
*/
private float mOutsideWidth;
private boolean canScrollBack = true;
private boolean canScroll = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
getWindow().setEnterTransition(new Slide(Gravity.RIGHT));
super.onCreate(savedInstanceState);
/**
* 把当前Activity加到列表里面
*/
mActivitys.add(this);
initScrollBack();
}

@Override
public void startActivity(Intent intent) {
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
}

/**
* 初始化左滑退出功能
*/
private void initScrollBack() {
mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
mOutsideWidth = -mWindowWidth / 4;
mOffsetX = mOutsideWidth;
mGestureDetector = new GestureDetector(this, new GestureListener());
mRootView = getWindow().getDecorView();

}

/**
* 控制上一个Activity移动
*/
private void beforeActivityTranslationX(float translationX) {
if (mBeforeActivity != null) {
mBeforeActivity.getRootView().setTranslationX(translationX);
}
}

/**
* 设置是否能滑动
*
* @param canScrollBack true 可以滑动
*/
protected void setCanScrollBack(boolean canScrollBack) {
this.canScrollBack = canScrollBack;
}

public View getRootView() {
return mRootView;
}

@Override
public void finish() {
mActivitys.remove(this);
if (mOffsetX < 0.0001 || mOffsetX > 0.0001) {
beforeActivityTranslationX(0);
}
super.finish();
}

/**
* 控制分发事件
*/
@Override
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
if (canScrollBack && ev.getX() < mWindowWidth / 10) {
if (mActivitys.size() > 1) {
mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
beforeActivityTranslationX(mOutsideWidth);
}
canScroll = true;
return onTouchEvent(ev);
}
return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (canScrollBack && canScroll) {
if (event.getAction() == MotionEvent.ACTION_UP && isScroll) {
isScroll = false;
canScroll = false;
//退出当前Activity
if (event.getX() > mWindowWidth / 2) {
if (mBeforeActivity != null) {
ObjectAnimator.ofFloat(mBeforeActivity.getRootView(), "translationX", mOffsetX, 0).setDuration(500).start();
}
ObjectAnimator moveIn = ObjectAnimator.ofFloat(mRootView, "translationX", event.getX(), mWindowWidth);
moveIn.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
finish();
}
});
moveIn.setDuration(500).start();
//反弹回来
} else if (event.getX() < mWindowWidth / 2) {
ObjectAnimator.ofFloat(mRootView, "translationX", event.getX(), 0).setDuration(500).start();
if (mBeforeActivity != null) {
ObjectAnimator.ofFloat(mBeforeActivity.getRootView(), "translationX", mOffsetX, mOutsideWidth).setDuration(500).start();
}
mOffsetX = mOutsideWidth;
}
} else {
mGestureDetector.onTouchEvent(event);
}
}
return true;
}

/**
* 手势监听
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 != null) {
handlerCurrentActivityScroll(e2);
handleBeforeActivityScroll(e2, distanceX);
}
return super.onScroll(e1, e2, distanceX, distanceY);
}

/**
* 处理当前页面滑动
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
isScroll = true;
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}

/**
* 处理上一个页面滑动
*/
private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
if (mBeforeActivity != null) {
mOffsetX = distanceX < 0 ? mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
if (mOffsetX > 0.0001) {
mOffsetX = 0f;
}
mBeforeActivity.getRootView().setTranslationX(mOffsetX);
}
}
}
}

这是我们最终的效果图
最终效果

DEMO