Android中SwipeBack實(shí)現(xiàn)右滑返回效果
現(xiàn)在有很多App支持右滑返回,比如知乎,效果比較贊。
于是自己對(duì)Activity和Fragment進(jìn)行了繼承,派生出SwipeBackActivity和SwipeBackFragment,用于對(duì)這種效果的實(shí)現(xiàn),也就是只要繼承這兩個(gè)類就可以了。
效果如下
- Activity
Fragment
Frgament的效果實(shí)現(xiàn)比Activity稍微簡(jiǎn)單,因?yàn)锳ctivity要考慮到dectorView。
支持滑動(dòng)的控件SwipeLayout,核心思路就是把原有的控件添加到支持滑動(dòng)的控件中,SwipeLayout要注意計(jì)算手勢(shì)速度,源碼如下:
package com.ui.jerry.swipebackdemo; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.Scroller; import android.widget.Toast; public class SwipeLayout extends LinearLayout { public static final String TAG = "SwipeLayout"; private View mEmptyView; private View mContentView; private int mLeftEdge; private int mWidth; private int mMaxScrollX; private Scroller mScroller; private VelocityTracker mVelocityTracker = null; private int mMaxFlingVelocity; private int mLastX; ViewGroup.LayoutParams childParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); private Context mContext; public static final int DURATION = 1500; //滿屏滑動(dòng)時(shí)間 public static final int OPEN_ANIM_DURATION = 1000; public static int SNAP_VELOCITY = 600; //最小的滑動(dòng)速率 private OnFinishListener mOnFinishListener; public SwipeLayout(Context context) { this(context, null); } public SwipeLayout(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } public void setOnFinishListener(OnFinishListener mOnFinishListener) { this.mOnFinishListener = mOnFinishListener; } void init() { mScroller = new Scroller(mContext); mMaxFlingVelocity = ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity(); mWidth = DisplayUtils.getScreenWidth(mContext) * 2; mMaxScrollX = mWidth / 2; mLeftEdge = mMaxScrollX - mMaxScrollX / 3; setOrientation(LinearLayout.HORIZONTAL); childParams.width = DisplayUtils.getScreenWidth(mContext); mEmptyView = LayoutInflater.from(mContext).inflate(R.layout.view_translate, null); addView(mEmptyView, childParams); } public void setContentView(View contentView) { if (mContentView != null) { removeView(mContentView); } mContentView = contentView; addView(contentView, childParams); postDelayed(new Runnable() { @Override public void run() { openActivityAnimation(); } }, 200); } /** * 獲取速度追蹤器 * * @return */ private VelocityTracker getVelocityTracker() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } return mVelocityTracker; } /** * 回收速度追蹤器 */ private void recycleVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.clear(); mVelocityTracker.recycle(); mVelocityTracker = null; } } @Override public boolean onTouchEvent(MotionEvent ev) { //1.獲取速度追蹤器 getVelocityTracker(); //2.將當(dāng)前事件納入到追蹤器中 mVelocityTracker.addMovement(ev); int pointId = -1; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //如果屏幕的動(dòng)畫還沒(méi)結(jié)束,你就按下了,我們就結(jié)束上一次動(dòng)畫,即開始這次新ACTION_DOWN的動(dòng)畫 // clearScrollHis(); mLastX = (int) ev.getX(); pointId = ev.getPointerId(0); break; case MotionEvent.ACTION_MOVE: int nextScrollX = (int) (mLastX - ev.getX() + getScrollX()); if (scrollTo(nextScrollX)) { mLastX = (int) ev.getX(); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: //3.計(jì)算當(dāng)前速度 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity); //獲取x y方向上的速度 float vX = mVelocityTracker.getXVelocity(pointId); Log.i(TAG, "mVelocityX:" + vX); //大于某個(gè)速率 直接滑動(dòng) if (vX > SNAP_VELOCITY) { scrollToLeft(); } else if (vX < -SNAP_VELOCITY) { scrollToRight(); } else { snapToDestation(); } //4.回收速度追蹤器 recycleVelocityTracker(); break; } return true; } private void openActivityAnimation() { clearScrollHis(); mScroller.startScroll(getScrollX(), 0, mMaxScrollX - getScrollX(), 0, OPEN_ANIM_DURATION); invalidate();//這里必須調(diào)用invalidate()才能保證computeScroll()會(huì)被調(diào)用,否則不一定會(huì)刷新界面,看不到滾動(dòng)效果 } public void closeActivityAnimation() { clearScrollHis(); mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, OPEN_ANIM_DURATION); invalidate();//這里必須調(diào)用invalidate()才能保證computeScroll()會(huì)被調(diào)用,否則不一定會(huì)刷新界面,看不到滾動(dòng)效果 } private void clearScrollHis() { if (mScroller != null) { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } } } /** * 根據(jù)現(xiàn)在的滾動(dòng)位置判斷 */ private void snapToDestation() { int scrollX = getScrollX(); if (scrollX > 0 && scrollX <= mLeftEdge) { smoothScrollTo(0); } else if (scrollX > mLeftEdge) { smoothScrollTo(mMaxScrollX); } } /** * 直接滾動(dòng) * * @param x * @return */ public boolean scrollTo(int x) { if (x < 0) { scrollTo(0, 0); } else if (x > mMaxScrollX) { scrollTo(mMaxScrollX, 0); } else { scrollTo(x, 0); } return true; } public void scrollToRight() { smoothScrollTo(mMaxScrollX); } public void scrollToLeft() { smoothScrollTo(0); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); Log.d(TAG, "left:" + l); if (l == 0) { Log.d(TAG, "OnFinish"); Toast.makeText(mContext, "Finished", Toast.LENGTH_SHORT).show(); if(mOnFinishListener!=null){ mOnFinishListener.onFinish(); } } } public void smoothScrollTo(int fx) { if (fx < 0) { smoothScrollTo(0, 0); } else if (fx > mMaxScrollX) { smoothScrollTo(mMaxScrollX, 0); } else { smoothScrollTo(fx, 0); } } // //調(diào)用此方法滾動(dòng)到目標(biāo)位置 public void smoothScrollTo(int fx, int fy) { int dx = fx - getScrollX(); int dy = fy - getScrollY(); smoothScrollBy(dx, dy); } //調(diào)用此方法設(shè)置滾動(dòng)的相對(duì)偏移 public void smoothScrollBy(int dx, int dy) { //設(shè)置mScroller的滾動(dòng)偏移量 mScroller.startScroll(getScrollX(), 0, dx, dy, Math.abs(dx * DURATION / mMaxScrollX)); invalidate();//這里必須調(diào)用invalidate()才能保證computeScroll()會(huì)被調(diào)用,否則不一定會(huì)刷新界面,看不到滾動(dòng)效果 } @Override public void computeScroll() { //先判斷mScroller滾動(dòng)是否完成 if (mScroller.computeScrollOffset()) { //這里調(diào)用View的scrollTo()完成實(shí)際的滾動(dòng) scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //必須調(diào)用該方法,否則不一定能看到滾動(dòng)效果 postInvalidate(); } super.computeScroll(); } /** * fragment或者activity 結(jié)束的接口 */ public interface OnFinishListener{ void onFinish(); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
Android自定義view制作抽獎(jiǎng)轉(zhuǎn)盤
這篇文章主要為大家詳細(xì)介紹了Android自定義view制作抽獎(jiǎng)轉(zhuǎn)盤,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12安卓圖片反復(fù)壓縮后為什么普遍會(huì)變綠而不是其它顏色?
今天小編就為大家分享一篇關(guān)于安卓圖片反復(fù)壓縮后為什么普遍會(huì)變綠而不是其它顏色?,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12android動(dòng)態(tài)壁紙調(diào)用的簡(jiǎn)單實(shí)例
動(dòng)態(tài)壁紙的實(shí)現(xiàn)其實(shí)就是在Activity中調(diào)用動(dòng)態(tài)壁紙服務(wù),通過(guò)綁定服務(wù)得到IWallpaperService,調(diào)用該接口中的attach函數(shù)實(shí)現(xiàn)壁紙的調(diào)用。2013-06-06Android studio 4.1打包失敗和插件錯(cuò)誤提示的解決
這篇文章主要介紹了Android studio 4.1打包失敗和插件錯(cuò)誤提示的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Android 實(shí)現(xiàn)局部圖片滑動(dòng)指引效果
這篇文章主要介紹了Android 實(shí)現(xiàn)局部圖片滑動(dòng)指引效果的相關(guān)資料,需要的朋友可以參考下2017-01-01自定義Adapter并通過(guò)布局泵LayoutInflater抓取layout模板編輯每一個(gè)item實(shí)現(xiàn)思路
自定義Adapter并通過(guò)布局泵LayoutInflater抓取layout模板編輯每一個(gè)item,下面我們開始學(xué)習(xí)這一篇的內(nèi)容,感興趣的朋友可以了解下哈2013-06-06更新Android Studio 3.0碰到的問(wèn)題小結(jié)
本文是小編給大家分享的更新Android Studio 3.0碰到的問(wèn)題小結(jié),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-11-11Android EditText隨輸入法一起移動(dòng)并懸浮在輸入法之上的示例代碼
這篇文章主要介紹了Android EditText隨輸入法一起移動(dòng)并懸浮在輸入法之上,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06