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)畫還沒結(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使用Dialog風(fēng)格彈出框的Activity
這篇文章主要為大家詳細(xì)介紹了Android使用Dialog風(fēng)格彈出框的Activity,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android ListView數(shù)據(jù)綁定顯示的三種解決方法
本篇文章小編為大家介紹,Android ListView數(shù)據(jù)綁定顯示的三種解決方法。需要的朋友參考下2013-04-04
Android Studio進(jìn)行APP圖標(biāo)更改的兩種方式總結(jié)
這篇文章主要介紹了Android Studio進(jìn)行APP圖標(biāo)更改的兩種方式總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例
本篇文章主要介紹了Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-11-11
fragment實(shí)現(xiàn)隱藏及界面切換效果
這篇文章主要為大家詳細(xì)介紹了fragment實(shí)現(xiàn)隱藏及界面切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
在Flutter中制作翻轉(zhuǎn)卡片動(dòng)畫的完整實(shí)例代碼
最近Flutter的勢(shì)頭是越來越猛了,作為一個(gè)Android程序猿,我自然也是想要趕緊嘗試一把,這篇文章主要給大家介紹了關(guān)于在Flutter中制作翻轉(zhuǎn)卡片動(dòng)畫的相關(guān)資料,需要的朋友可以參考下2021-10-10
詳解VirtualApk啟動(dòng)插件Activity
這篇文章主要介紹了詳解VirtualApk啟動(dòng)插件Activity,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Android實(shí)現(xiàn)無限循環(huán)滾動(dòng)彈幕的代碼示例
這篇文章主要介紹了android實(shí)現(xiàn)無限循環(huán)滾動(dòng)的彈幕2024-08-08
,文中通過代碼示例講解的非常詳細(xì),對(duì)大家實(shí)現(xiàn)循環(huán)彈幕有一定的幫助,需要的朋友可以參考下

