Activity右滑返回的实现
转载请注明出处: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));
}
}
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(View view)方法会把view放到bgLayout中。
setContentView(R.layout.layout_second);
// 获得decorView
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){// 当按下时
// 获得按下时的X坐标
downX = event.getX();
}else if(event.getAction() == MotionEvent.ACTION_MOVE){// 当手指滑动时
// 获得滑过的距离
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > 0){// 如果是向右滑动
decorView.setX(moveDistanceX); // 设置界面的X到滑动到的位置
}
}else if(event.getAction() == MotionEvent.ACTION_UP){// 当抬起手指时
// 获得滑过的距离
float moveDistanceX = event.getX() - downX;
if(moveDistanceX > screenWidth / 2){
// 如果滑动的距离超过了手机屏幕的一半, 结束当前Activity
finish();
}else{ // 如果滑动距离没有超过一半
// 恢复初始状态
decorView.setX(0);
}
}
return super.onTouchEvent(event);
}
}
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(View view)方法会把view放到bgLayout中。
setContentView(R.layout.layout_second);
// 获得decorView
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){
// 如果滑动的距离超过了手机屏幕的一半, 滑动处屏幕后再结束当前Activity
continueMove(moveDistanceX);
}else{
// 如果滑动距离没有超过一半, 往回滑动
rebackToLeft(moveDistanceX);
}
}
return super.onTouchEvent(event);
}
/**
* 从当前位置一直往右滑动到消失。
* 这里使用了属性动画。
*/
private void continueMove(float moveDistanceX){
// 从当前位置移动到右侧。
ValueAnimator anim = ValueAnimator.ofFloat(moveDistanceX, screenWidth);
anim.setDuration(1000); // 一秒的时间结束, 为了简单这里固定为1秒
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 位移
float x = (float) (animation.getAnimatedValue());
decorView.setX(x);
}
});
// 动画结束时结束当前Activity
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();
}
}
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>
读者可以把SecondActivity单独封装好,以后使用时直接继承该类就可以了实现滑动返回了。
例子中并没有实现阴影效果,因为阴影效果有多种实现方式。读者可以自己实现。如果要实现文章开始的那种阴影,那我们移动的就不应该是decorView了,而是android.R.id.content对应的FrameLayout。并且对它父布局的背景色随着滑动的距离进行渐变。
3. 存在的问题
如果读者自己去实现该类,并在里面添加了一个ListView,或者ScrollView等可以滑动的视图。就有可能出现ListView可以滑动,但是我们的Activity并不能滑动返回了。
这是一个关于View的滑动冲突的问题,下一篇文章中,我们将一起研究View的事件分发机制。
转载请注明出处:http://blog.csdn.net/h28496/article/details/49227419
来源:CSDN
作者:木质的旋律
链接:https://blog.csdn.net/H28496/article/details/49227419