Android實(shí)現(xiàn)微信側(cè)滑關(guān)閉頁面效果
最近手機(jī)升級(jí)了5.0系統(tǒng)后,突然間發(fā)現(xiàn)微信竟然有IOS一樣的側(cè)滑關(guān)閉當(dāng)前頁面的效果,就想把這種效果也加進(jìn)自己的項(xiàng)目里面。本著不重復(fù)造輪子的原則,就在網(wǎng)上百度了很久,發(fā)現(xiàn)大多數(shù)人都是采用自定義View來實(shí)現(xiàn),但是對(duì)于我這種已經(jīng)基本完成的項(xiàng)目來說,如果全部的Activity再重新使用自定義的View無疑是一種可怕的噩夢。
因此,我這里實(shí)現(xiàn)了另外一種不需要自定義View也能實(shí)現(xiàn)的方法,其子類只要繼承于它,便能擁有其側(cè)滑滑動(dòng)的功能。
隨便說一句,此方法僅對(duì)5.0以上的手機(jī)有效(反正微信也是5.0上才能用),5.0以下的請(qǐng)無視?。?!
原理
在每個(gè)Activity里面都有一個(gè)底層的View,也就是所謂的rootView,當(dāng)我們加載一個(gè)xml布局時(shí),系統(tǒng)就會(huì)自動(dòng)給你生成這個(gè)rootView,由于它是一個(gè)View,那么也就意味著你可以通過一定的代碼隨意移動(dòng)這個(gè)根布局。
如下代碼所示,只要簡單的幾行代碼便能實(shí)現(xiàn)布局的移動(dòng)。
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);
}
/**
* 手勢監(jiān)聽
*/
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);
}
/**
* 處理當(dāng)前頁面滑動(dòng)
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}
}
}
這是我們的效果

Activity聯(lián)動(dòng)
這差距還是很大的,最明顯的地方是,我們移動(dòng) 的時(shí)候,上一層Activity竟然沒有跟著聯(lián)動(dòng)。
解決這個(gè)問題的方法也簡單,如圖所示,每當(dāng)啟動(dòng)一個(gè)Activity時(shí),系統(tǒng)都會(huì)把Activity放到一個(gè)棧里面,由于棧的工作原理可知,APP里面的Activity是一層覆蓋一層的,就如上圖所示。為此,每當(dāng)啟動(dòng)一個(gè)Activity時(shí),就可以把當(dāng)前的Actiivty存儲(chǔ)到一個(gè)List里面,這樣,我們就可以在當(dāng)前的Activity里面取出上一個(gè)Activity進(jìn)行操作。
因此,在進(jìn)入一個(gè)新的Activity的時(shí)候,在其onCreate方法里面把當(dāng)前的Activity加載到列表里,當(dāng)退出時(shí),在finish的重載方法里面,將當(dāng)前Activity從列表里面移除。
注意?。。≡诨瑒?dòng)的時(shí)候必須需要考慮到Activity里面有可能會(huì)有類似于ListView一類的滑動(dòng)控件,因此,我們必須對(duì)事件進(jìn)行分發(fā)控制。
代碼如下
/**
* Created by yuyu on 2015/10/29.
*/
public class TestActivity extends AppCompatActivity {
View mRootView;
private GestureDetector mGestureDetector;
private static List<TestActivity> mActivitys = new ArrayList<>();
/**
* 移動(dòng)距離
*/
private float mWindowWidth;
private TestActivity mBeforeActivity;
/**
* 上一個(gè)Activity偏移量
*/
private float mOffsetX;
/**
* 上一個(gè)頁面移出的位置
*/
private float mOutsideWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
/**
* 把當(dāng)前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);
}
/**
* 控制分發(fā)事件,在這里控制能能觸發(fā)拖動(dòng)的范圍
*/
@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;
}
/**
* 控制上一個(gè)Activity移動(dòng)
*/
private void beforeActivityTranslationX(float translationX) {
if (mBeforeActivity != null) {
mBeforeActivity.getRootView().setTranslationX(translationX);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
/**
* 手勢監(jiān)聽
*/
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);
}
/**
* 處理當(dāng)前頁面滑動(dòng)
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}
/**
* 處理上一個(gè)頁面滑動(dòng)
*/
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);
}
}
}
}
這是聯(lián)動(dòng)后的效果圖

現(xiàn)在算是有點(diǎn)效果了,但是和微信的差距還是很大,接下來我們便需要開始處理自動(dòng)滑動(dòng)了
自動(dòng)滑動(dòng)
這個(gè)就不需要多說了,這個(gè)主要就是利用屬性動(dòng)畫進(jìn)行移動(dòng)
以下是完整的代碼
public class SlideActivity extends AppCompatActivity {
private static final String TAG = "SlideActivity";
private static List<SlideActivity> mActivitys = new ArrayList<>();
/**
* 手勢監(jiān)聽
*/
private GestureDetector mGestureDetector;
private View mRootView;
private boolean isScroll = false;
/**
* 移動(dòng)距離
*/
private float mWindowWidth;
private SlideActivity mBeforeActivity;
/**
* 上一個(gè)Activity偏移量
*/
private float mOffsetX;
/**
* 上一個(gè)頁面移出的位置
*/
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);
/**
* 把當(dāng)前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();
}
/**
* 控制上一個(gè)Activity移動(dòng)
*/
private void beforeActivityTranslationX(float translationX) {
if (mBeforeActivity != null) {
mBeforeActivity.getRootView().setTranslationX(translationX);
}
}
/**
* 設(shè)置是否能滑動(dòng)
*
* @param canScrollBack true 可以滑動(dòng)
*/
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();
}
/**
* 控制分發(fā)事件
*/
@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;
//退出當(dāng)前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;
}
/**
* 手勢監(jiān)聽
*/
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);
}
/**
* 處理當(dāng)前頁面滑動(dòng)
*/
private void handlerCurrentActivityScroll(MotionEvent e2) {
isScroll = true;
mRootView.setTranslationX(e2.getX());
if (e2.getX() > mWindowWidth - 20) {
finish();
}
}
/**
* 處理上一個(gè)頁面滑動(dòng)
*/
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);
}
}
}
}
這是我們最終的效果圖

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android?NDK開發(fā)之FFmpeg視頻添加水印
這篇文章主要介紹了在Android?NDK開發(fā)中如何通過FFmpeg為視頻添加水印,文中的示例代碼講解詳細(xì),對(duì)我們了解Android開發(fā)有一定的幫助,感興趣的可以學(xué)習(xí)一下2021-12-12
Spi機(jī)制在Android開發(fā)的應(yīng)用示例詳解
這篇文章主要為大家介紹了Spi機(jī)制在Android開發(fā)的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Android 沉浸式改變小米魅族狀態(tài)欄顏色的實(shí)例代碼
這篇文章主要介紹了Android 沉浸式改變小米魅族狀態(tài)欄顏色的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
Android編程實(shí)現(xiàn)自定義分享列表ACTION_SEND功能的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)自定義分享列表ACTION_SEND功能的方法,結(jié)合實(shí)例形式詳細(xì)分析了自定義分享列表功能的步驟與具體操作技巧,需要的朋友可以參考下2017-02-02
Android Studio手動(dòng)配置Gradle的方法
Gradle:Gradle是一個(gè)基于Apache Ant和Apache Maven概念的項(xiàng)目自動(dòng)化建構(gòu)工具。它使用一種基于Groovy的特定領(lǐng)域語言(DSL)來聲明項(xiàng)目設(shè)置,拋棄了基于XML的各種繁瑣配置,本文給大家介紹Android Studio手動(dòng)配置Gradle的方法,一起看看吧2017-11-11
Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例
這篇文章主要介紹了Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例,本文給出了服務(wù)端代碼和客戶端代碼,需要的朋友可以參考下2014-10-10
Android Studio ADB網(wǎng)絡(luò)調(diào)試匯總
這篇文章主要為大家詳細(xì)介紹了Android Studio ADB網(wǎng)絡(luò)調(diào)試的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05

