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

Activity聯(lián)動
這差距還是很大的,最明顯的地方是,我們移動 的時候,上一層Activity竟然沒有跟著聯(lián)動。
解決這個問題的方法也簡單,如圖所示,每當(dāng)啟動一個Activity時,系統(tǒng)都會把Activity放到一個棧里面,由于棧的工作原理可知,APP里面的Activity是一層覆蓋一層的,就如上圖所示。為此,每當(dāng)啟動一個Activity時,就可以把當(dāng)前的Actiivty存儲到一個List里面,這樣,我們就可以在當(dāng)前的Activity里面取出上一個Activity進(jìn)行操作。
因此,在進(jìn)入一個新的Activity的時候,在其onCreate方法里面把當(dāng)前的Activity加載到列表里,當(dāng)退出時,在finish的重載方法里面,將當(dāng)前Activity從列表里面移除。
注意?。。≡诨瑒拥臅r候必須需要考慮到Activity里面有可能會有類似于ListView一類的滑動控件,因此,我們必須對事件進(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<>();
/**
* 移動距離
*/
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);
/**
* 把當(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ā)拖動的范圍
*/
@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);
}
/**
* 手勢監(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)前頁面滑動
*/
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);
}
}
}
}
這是聯(lián)動后的效果圖

現(xiàn)在算是有點(diǎn)效果了,但是和微信的差距還是很大,接下來我們便需要開始處理自動滑動了
自動滑動
這個就不需要多說了,這個主要就是利用屬性動畫進(jìn)行移動
以下是完整的代碼
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;
/**
* 移動距離
*/
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);
/**
* 把當(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();
}
/**
* 控制上一個Activity移動
*/
private void beforeActivityTranslationX(float translationX) {
if (mBeforeActivity != null) {
mBeforeActivity.getRootView().setTranslationX(translationX);
}
}
/**
* 設(shè)置是否能滑動
*
* @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();
}
/**
* 控制分發(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)前頁面滑動
*/
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);
}
}
}
}
這是我們最終的效果圖

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android?NDK開發(fā)之FFmpeg視頻添加水印
這篇文章主要介紹了在Android?NDK開發(fā)中如何通過FFmpeg為視頻添加水印,文中的示例代碼講解詳細(xì),對我們了解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í)例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-02-02
Android編程實(shí)現(xiàn)自定義分享列表ACTION_SEND功能的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)自定義分享列表ACTION_SEND功能的方法,結(jié)合實(shí)例形式詳細(xì)分析了自定義分享列表功能的步驟與具體操作技巧,需要的朋友可以參考下2017-02-02
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)試的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05

