转载请注明出处:http://blog.csdn.net/h28496/article/details/49227419
1. 滑动返回的效果
效果描述:
- 从左侧向右滑动将当前Activity向右移动,并显示出下方的Activity。
- 在移动的过程中,透明部分有透明度的变化。
2. 如何使得Activity滑动?
1. 要滑动的是什么?
我们要滑动的是整个Activity的视图。不太清楚Activity视图结构的可以看一下下面这张图:
一般在代码中通过setContentView(View view)把view放到了android.R.id.content对应的FrameLayout中。
与该FrameLayout平级的还有它下面那些View,比如ActionBar等。
如果我们要使得整个Activity的界面滑动,就需要使得根布局decorView滑动。
通过在Activity中用如下代码可以获得decorView:
View decorView = getWindow().getDecorView();
2. 具体怎么滑动?
- 当手指按下时,获得按下时的坐标(xDown, yDown)
- 手指移动时,获得当前手指坐标(xCurrent, yCurrent),可以得到水平移动距离 distanceX = xCurrent - xDown
- 获得水平移动距离之后,通过setX()方法设置界面的X坐标。
3. 如何获得手指的位置?
在Activity的onTouchEvent(MotionEvent event)方法中即可获得手指的位置。
event.getX() 为手指的X坐标;
event.getY() 为手指的Y坐标。
4. 抬起手指后的行为
手指抬起时,应该恢复到初始位置还是结束当前Activity呢?这个可以随意设置了。假设我们以屏幕的一半为界限。
1. 当滑动超过一半时,松开手后,界面继续向右侧滑动直到完全在屏幕上不可见,这时调用finish()。
2. 当滑动没有超过一半时,松开手后,界面返回到初始状态,即x坐标为0。
3. 一个简单的滑动返回效果
效果描述:
FirstActivity 的布局是 layout_first.xml
SecondActivity 的布局是 layout_second.xml
FirstActivity 中有一个按钮,点击跳转到 SecondActivity
在 SecondActivity 右滑返回到 FirstActivity
1. 布局文件: layout_first.xml (背景蓝色)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#8888ff"
tools:context="${relativePackage}.${activityClass}" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="跳转到SecondActivity" />
</RelativeLayout>
2. 布局文件: layout_second.xml (背景红色)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff8888"
tools:context="${relativePackage}.${activityClass}" >
</RelativeLayout>
3. 代码:Activity_First.java
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class FirstActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_first);
}
public void nextActivity(View v) {
startActivity(new Intent(this, SecondActivity.class));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
4. 代码:Activity_Second.java
注释有点多。
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
/**
* @author 郑海鹏
* @since 2015年10月16日
*/
public class SecondActivity extends Activity {
/**
* 整个Activity视图的根视图
*/
View decorView;
/**
* 手指按下时的坐标
*/
float downX, downY;
/**
* 手机屏幕的宽度和高度
*/
float screenWidth, screenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_second);
decorView = getWindow().getDecorView();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
}
/**
* 通过重写该方法,对触摸事件进行处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
downX = event.getX();
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > 0){
decorView.setX(moveDistanceX);
}
}else if(event.getAction() == MotionEvent.ACTION_UP){
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > screenWidth / 2){
finish();
}else{
decorView.setX(0);
}
}
return super.onTouchEvent(event);
}
}
- 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
5. 效果展示一
可以看出,SecondActivity是随着我们的手指在滑动的,但是FirstActivity却并没有在SecondActivity滑动时显示出来。
原因是:SecondActivity的背景不是透明的。
6. 背景透明
用主题可以使得Activity的背景也是透明的。
首先自定义一个style,在res/values/styles.xml文件中(如果没有自己建一个)加上一个主题:
<style name="MyTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
并且在AndroidManifest.xml中,为我们的SecondActivity设置刚才的主题:
<activity
android:name="zhp.test.decorviewtest.SecondActivity"
android:theme="@style/MyTheme" >
</activity>
再次运行就得到了下面的效果了:
3. 带有动画效果的滑动返回
细心的读者应该发现了,上面滑动返回的效果是很生硬的。 松手之后,如果滑动距离小于屏幕一半的宽度直接回到原始位置。同样,超过一半时,直接结束了。
我们可以为decorView设置一个动画,让它在回复初始位置时,缓慢地滑动回去,在结束时,也滑动处屏幕后再结束当前Activity。关于属性动画,参见我的博客:【安卓】属性动画
1. 改进后的SecondActivity.java
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
/**
* @author 郑海鹏
* @since 2015年10月16日
*/
public class SecondActivity extends Activity {
View decorView;
float downX, downY;
float screenWidth, screenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_second);
decorView = getWindow().getDecorView();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
}
/**
* 通过重写该方法,对触摸事件进行处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
downX = event.getX();
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > 0){
decorView.setX(moveDistanceX);
}
}else if(event.getAction() == MotionEvent.ACTION_UP){
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > screenWidth / 2){
continueMove(moveDistanceX);
}else{
rebackToLeft(moveDistanceX);
}
}
return super.onTouchEvent(event);
}
/**
* 从当前位置一直往右滑动到消失。
* 这里使用了属性动画。
*/
private void continueMove(float moveDistanceX){
ValueAnimator anim = ValueAnimator.ofFloat(moveDistanceX, screenWidth);
anim.setDuration(1000);
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float x = (float) (animation.getAnimatedValue());
decorView.setX(x);
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finish();
}
});
}
/**
* Activity被滑动到中途时,滑回去~
*/
private void rebackToLeft(float moveDistanceX){
ObjectAnimator.ofFloat(decorView, "X", moveDistanceX, 0).setDuration(300).start();
}
}
- 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
2. 效果展示三
5. 总结
1. 实现滑动返回的步骤
- 重写onTouchEvent(MotionEvent event)方法,使得Activity可以随着手指的滑动而滑动
- 为Activity设置一个透明背景的主题
- 为了更好的效果,我们可以为滑动添加一些动画效果
2. 其他效果和扩展
- 在示例代码中,用到的主题是继承自@android:style/Theme.Black.NoTitleBar.Fullscreen的,读者也可以根据自己的需要继承自其他主题,还可以添加其他的item。
例如,下面这个主题就是用于滑动返回+Toolbar+沉浸式状态栏的, 也就是文章开始的示例图用到的主题:
<style name="ToolBarAndTranslucent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:listDivider">@drawable/divider</item>
</style>
3. 存在的问题
如果读者自己去实现该类,并在里面添加了一个ListView,或者ScrollView等可以滑动的视图。就有可能出现ListView可以滑动,但是我们的Activity并不能滑动返回了。
这是一个关于View的滑动冲突的问题,下一篇文章中,我们将一起研究View的事件分发机制。
转载请注明出处:http://blog.csdn.net/h28496/article/details/49227419