翻看今日頭條app時發現右滑Activity退出,近日領導又提到這個功能,本以爲是activity自帶的特效(改下某項設置後能達到效果),百度後發現很多bug,尤其是結合listview導致滑動事件衝突。
設計思路:
- BaseActivity作爲所有Activity基類繼承AppCompatActivity(該類繼承FragmentActivity),目的方便所有Activity實現該效果。
- 所有Activity的View是DecorVIew的子view,我們獲取該DecorVIew對其滑動操作即可。
- BaseActivity重寫dispatchTouchEvent,不能重寫onTouchEvent(解決與listview滑動衝突問題),實現DecorView的滑動。
滑動DecorView後發現並不能看到下部Activity,我們需要設置其背景透明。
先上效果
BaseActivity代碼:
package smsread.cn.zkr.com.smsread;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
public class BaseActivity extends AppCompatActivity {
View decorView;
int screenWidth;//屏寬
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
decorView=getWindow().getDecorView();
DisplayMetrics metrics=new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenWidth=metrics.widthPixels;
}
float startX,startY,endX,endY,distanceX,distanceY;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
startX=event.getX();
startY=event.getY();
break;
case MotionEvent.ACTION_MOVE:
endX=event.getX();
endY=event.getY();
distanceX=endX-startX;
distanceY=Math.abs(endY-startY);
//1.判斷手勢右滑 2.橫向滑動的距離要大於豎向滑動的距離
if(endX-startX>0&&distanceY<distanceX){
decorView.setX(distanceX);
}
break;
case MotionEvent.ACTION_UP:
endX=event.getX();
distanceX=endX-startX;
endY=event.getY();
distanceY=Math.abs(endY-startY);
//1.判斷手勢右滑 2.橫向滑動的距離要大於豎向滑動的距離 3.橫向滑動距離大於屏幕三分之一才能finish
if(endX-startX>0&&distanceY<distanceX&&distanceX>screenWidth/3){
moveOn(distanceX);
}
//1.判斷手勢右滑 2.橫向滑動的距離要大於豎向滑動的距離 但是橫向滑動距離不夠則返回原位置
else if(endX-startX>0&&distanceY<distanceX){
backOrigin(distanceX);
}else{
decorView.setX(0);
}
break;
}
return super.dispatchTouchEvent(event);
}
/**
* 返回原點
* @param distanceX 橫向滑動距離
*/
private void backOrigin(float distanceX){
ObjectAnimator.ofFloat(decorView,"X",distanceX,0).setDuration(300).start();
}
/**
* 劃出屏幕
* @param distanceX 橫向滑動距離
*/
private void moveOn(float distanceX){
ValueAnimator valueAnimator=ValueAnimator.ofFloat(distanceX,screenWidth);
valueAnimator.setDuration(300);
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
decorView.setX((Float) animation.getAnimatedValue());
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
finish();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
背景設置透明需要在res/style文件中添加主題樣式,將主題樣式設置爲背景透明,其parent爲app的theme,然後在manifest.xml中設置每個activity的theme。注意:不能直接在app的theme下直接設置背景透明,有興趣的可以試試效果是什麼樣。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#ffe516</item>
<!--<item name="colorPrimaryDark">#ffe516</item>-->
<item name="colorAccent">#ffe516</item>
</style>
<style name="MyTheme" parent="AppTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
關於listview與BaseActivity出現的滑動衝突問題,可以看看android事件分發機制:http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html