Android實現(xiàn)微信側(cè)滑關(guān)閉頁面效果
最近手機升級了5.0系統(tǒng)后,突然間發(fā)現(xiàn)微信竟然有IOS一樣的側(cè)滑關(guān)閉當(dāng)前頁面的效果,就想把這種效果也加進自己的項目里面。本著不重復(fù)造輪子的原則,就在網(wǎng)上百度了很久,發(fā)現(xiàn)大多數(shù)人都是采用自定義View來實現(xiàn),但是對于我這種已經(jīng)基本完成的項目來說,如果全部的Activity再重新使用自定義的View無疑是一種可怕的噩夢。
因此,我這里實現(xiàn)了另外一種不需要自定義View也能實現(xiàn)的方法,其子類只要繼承于它,便能擁有其側(cè)滑滑動的功能。
隨便說一句,此方法僅對5.0以上的手機有效(反正微信也是5.0上才能用),5.0以下的請無視?。?!
原理
在每個Activity里面都有一個底層的View,也就是所謂的rootView,當(dāng)我們加載一個xml布局時,系統(tǒng)就會自動給你生成這個rootView,由于它是一個View,那么也就意味著你可以通過一定的代碼隨意移動這個根布局。
如下代碼所示,只要簡單的幾行代碼便能實現(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進行操作。
因此,在進入一個新的Activity的時候,在其onCreate方法里面把當(dāng)前的Activity加載到列表里,當(dāng)退出時,在finish的重載方法里面,將當(dāng)前Activity從列表里面移除。
注意!??!在滑動的時候必須需要考慮到Activity里面有可能會有類似于ListView一類的滑動控件,因此,我們必須對事件進行分發(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)在算是有點效果了,但是和微信的差距還是很大,接下來我們便需要開始處理自動滑動了
自動滑動
這個就不需要多說了,這個主要就是利用屬性動畫進行移動
以下是完整的代碼
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中使用SharedPreferences進行數(shù)據(jù)存儲的操作方法
本篇文章介紹了,在android中使用SharedPreferences進行數(shù)據(jù)存儲的操作方法。需要的朋友參考下2013-04-04Android時間日期拾取器學(xué)習(xí)使用(DatePicker、TimePicker)
這篇文章主要為大家詳細介紹了Android提供的DatePicker日期拾取器和TimePicker時間拾取器的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02Android自定義控件eBook實現(xiàn)翻書效果實例詳解
這篇文章主要介紹了Android自定義控件eBook實現(xiàn)翻書效果的方法,結(jié)合實例形式分析了Android自定義控件實現(xiàn)翻書效果的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下2016-10-10Android P實現(xiàn)靜默安裝的方法示例(官方Demo)
這篇文章主要介紹了Android P實現(xiàn)靜默安裝,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02