Android 一键打造仿IOS右滑退出Activity,非常简单的集成方式(SwipeFinishLayout)

相信很多玩过苹果的伙伴,都觉得苹果应用的可以右滑退出界面非常方便,但是Android的应用大多都是需要点击返回按钮才能退出,不过现在已经有几个App有了,比如:网易、今日头条和淘宝都有实现。现在网上已经有了这个控件,已经实现了,但是使用起来不是很方便,今天我们就把这个控件的使用方式简单化,只需要你继承一个BaseActivity就可以完成右滑控件的添加。

首先看效果:
这里写图片描述

跟IOS的效果没有很大的区别吧?
下面看代码。
BaseSwipeFinishActivity:

public abstract class BaseSwipeFinishActivity extends Activity implements SwipeFinishLayout.SwipeToCloseLayoutAction {

    private SwipeFinishLayout mSwipeToCloseLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    /**
     * 此方法要在setContentView之后执行,用户添加右滑退出的布局。
     */
    protected void addSwipeFinishLayout() {
        mSwipeToCloseLayout = new SwipeFinishLayout(this);
        mSwipeToCloseLayout.attachToActivity(this);
        mSwipeToCloseLayout.setSwipeToCloseLayoutAction(this);
    }

    /**
     * 是否可滑动退出
     *
     * @param enableGesture
     */
    public void setEnableGesture(boolean enableGesture) {
        if (mSwipeToCloseLayout != null) {
            mSwipeToCloseLayout.setEnableGesture(enableGesture);
        }
    }

    /**
     * 全屏时滑动的处理
     *
     * @param fullScreen
     */
    public void setActivityFullScreen(boolean fullScreen) {
        if (mSwipeToCloseLayout != null) {
            mSwipeToCloseLayout.setActivityFullScreen(fullScreen);
        }
    }

    /**
     * 向右滑动是否可关闭activity
     */
    @Override
    public boolean onScrollToClose() {
        return true;
    }

    /**
     * 是否点击在可左右滑动的views里面
     */
    @Override
    public boolean inChild(Rect rect, Point point) {
        return false;
    }

    @Override
    public void onCloseAction() {
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSwipeToCloseLayout != null) {
            mSwipeToCloseLayout.removeAllViews();
            mSwipeToCloseLayout.setSwipeToCloseLayoutAction(null);
            mSwipeToCloseLayout = null;
        }
    }

    /**
     * 关闭执行的动画
     */
    @Override
    public final void finish() {
        super.finish();
        overridePendingTransition(R.anim.push_none, R.anim.push_up_out);
    }

    /**
     * 打开activity执行的方法
     */
    @Override
    public void startActivity(Intent intent) {
        super.startActivity(intent);
        overridePendingTransition(R.anim.push_up_in, R.anim.push_none);
    }

}

两个Activity代码:

public class MainActivity extends BaseSwipeFinishActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        addSwipeFinishLayout();
        //首页activtiy可设置为不能滑动关闭
        setEnableGesture(false);
    }

    public void onClick(View view) {
        startActivity(new Intent(this, SecondActivity.class));
    }
}


public class SecondActivity extends BaseSwipeFinishActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        //添加布局之后需要执行添加右滑退出的布局
        addSwipeFinishLayout();
    }
}

右滑控件代码:
SwipeFinishLayout

public class SwipeFinishLayout extends FrameLayout implements SwipeFinishAction {

    private boolean             mEnableGesture = true;
    private Scroller            mScroller;
    private SwipeFinishUtils mSwipeToClose;
    private boolean             mToCloseActivity;
    private Activity            mActivity;

    public SwipeFinishLayout(Context context) {
        super(context);
        init(context);
    }

    public SwipeFinishLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context){
        mScroller = new Scroller(context);
        mSwipeToClose = new SwipeFinishUtils(context, mScroller);

        mSwipeToClose.setContentView(this);
        mSwipeToClose.setSwipeToTouchAction(this);
        setWillNotDraw(false);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mEnableGesture && mSwipeToClose != null && mSwipeToClose.onInterceptTouchEvent(ev)) {
            return true;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return (mEnableGesture && mSwipeToClose != null && mSwipeToClose.onTouchEvent(ev));
    }

    public void setAllAreaCanScroll(boolean allAreaCanScroll) {
    }

    public void setEnableGesture(boolean enableGesture) {
        this.mEnableGesture = enableGesture;
    }

    public void setActivityFullScreen(boolean fullScreen){
        this.mSwipeToClose.setActivityFullScreen(fullScreen);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        } else {
            if (mToCloseActivity) {
                if (mSwipeToCloseLayoutAction != null) {
                    mSwipeToCloseLayoutAction.onCloseAction();
                }
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(Math.abs(getScrollX()) >= getWidth()){
            return;
        }
        canvas.save();
        int alpha = 160 * (getWidth() - Math.abs(getScrollX())) / getWidth();
        int color = (alpha << 24 | 0x00000000);
        canvas.drawColor(color);
        canvas.restore();
    }

    @Override
    public void startScroll(int startX, int dx, boolean finish) {
        mToCloseActivity = dx < 0;
        mScroller.startScroll(startX, 0, dx, 0, 500 * Math.abs(dx) / getWidth());
        postInvalidate();
    }

    private SwipeToCloseLayoutAction mSwipeToCloseLayoutAction;

    public void setSwipeToCloseLayoutAction(SwipeToCloseLayoutAction action) {
        this.mSwipeToCloseLayoutAction = action;
    }

    public interface SwipeToCloseLayoutAction extends SwipeFinishTouchCheckAction {
        /**
         * 关闭界面
         */
        void onCloseAction();
    }

    public void attachToActivity(Activity activity){
        mActivity = activity;
        ViewGroup viewGroup = (ViewGroup) activity.findViewById(android.R.id.content);
        View contentView = viewGroup.getChildAt(0);
        viewGroup.removeView(contentView);
        if(contentView != null){
            if(contentView.getBackground() == null){
                contentView.setBackgroundColor(Color.WHITE);
            }
            attachView(contentView);
        }
        viewGroup.addView(this);
        convertActivityFromTranslucent(activity);
    }

    public void attachView(View view) {
        addView(view);
    }
    public  void convertActivityFromTranslucent(Activity activity) {
        try {
            Method method = Activity.class
                    .getDeclaredMethod("convertFromTranslucent");
            method.setAccessible(true);
            method.invoke(activity);
        } catch (Throwable t) {
        }
    }
    public  void convertActivityToTranslucent(Activity activity) {
        if (activity == null)
            return;
        try {
            Class<?>[] t = Activity.class.getDeclaredClasses();
            Class<?> translucentConversionListenerClazz = null;
            Class<?>[] method = t;
            int len$ = t.length;
            for (int i$ = 0; i$ < len$; ++i$) {
                Class<?> clazz = method[i$];
                if (clazz.getSimpleName().contains(
                        "TranslucentConversionListener")) {
                    translucentConversionListenerClazz = clazz;
                    break;
                }
            }
            if (Build.VERSION.SDK_INT >= 21) {
                Class<?> ActivityOptions = Class
                        .forName("android.app.ActivityOptions");
                Method var8 = Activity.class.getDeclaredMethod(
                        "convertToTranslucent",
                        translucentConversionListenerClazz, ActivityOptions);
                var8.setAccessible(true);
                var8.invoke(activity, new Object[] { null, null });
            } else {
                Method var8 = Activity.class.getDeclaredMethod(
                        "convertToTranslucent",
                        translucentConversionListenerClazz);
                var8.setAccessible(true);
                var8.invoke(activity, new Object[] { null });
            }
        } catch (Throwable e) {
        }
    }
    @Override
    public void onScroll() {
        convertActivityToTranslucent(mActivity);
    }

    @Override
    public void onTouchDown() {
        convertActivityToTranslucent(mActivity);
    }

    @Override
    public boolean inChild(Rect rect, Point point) {
        return mSwipeToCloseLayoutAction != null && mSwipeToCloseLayoutAction.inChild(rect, point);
    }

    @Override
    public boolean onScrollToClose() {
        return mSwipeToCloseLayoutAction != null && mSwipeToCloseLayoutAction.onScrollToClose();
    }

    public void onActivityDestory() {
        mSwipeToClose = null;
        mScroller = null;
        mActivity = null;
        mToCloseActivity = false;
    }

}

SwipeFinishUtils,主要的滑动处理基本都在这个里面,根据手指滑动的距离进行判断是否要关闭activity,这里是根据是否滑动到了屏幕的一半来判断的。滑动的时候移动当前activity的布局:

package com.xw.widget;

import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Scroller;

import java.lang.reflect.Field;

public class SwipeFinishUtils {

    private static final int INVALID_POINTER = -1;

    private boolean mIsBeingDragged;

    private int mTouchSlop;
    private int mActivePointerId;
    private int mLastMotionX;
    private int mLastMotionY;
    private int mMoveMotionX;
    private int mMoveMotionY;
    private int mMaximumVelocity;
    private int mMinimumVelocity;

    private View mView;
    private VelocityTracker mVelocityTracker;
    private Scroller mScroller;
    private SwipeFinishAction mAction;
    private int mStatusBarHeight;

    private boolean mFullScreen;
    private boolean mDownOver = false;//距离左侧1/2才生效
    private int mScreenWidth;
    private boolean mInChild;

    public SwipeFinishUtils(Context context, Scroller scroller) {
        mScroller = scroller;
        mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mStatusBarHeight =getStatusBarHeight(context);
    }

    public void setContentView(View view) {
        this.mView = view;
    }

    public void setSwipeToTouchAction(SwipeFinishAction action) {
        this.mAction = action;
    }
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        Log.i("TAG", "SwipeToClose----------------------onInterceptTouchEvent:" + ev.getAction() + ",mIsBeingDragged:" + mIsBeingDragged);
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                if (!mDownOver) {
                    break;
                }
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    break;
                }

                final int pointerIndex = ev.findPointerIndex(activePointerId);
                if (pointerIndex == -1) {
                    break;
                }

                final int x = (int) ev.getX(pointerIndex);
                final int y = (int) ev.getY(pointerIndex);
                final int xDiff = Math.abs(x - mLastMotionX);
                final int yDiff = Math.abs(y - mLastMotionY);
                boolean hasScrollToRight = x > mLastMotionX;
                boolean hasCanScroll = !mInChild || (hasScrollToRight && mAction != null && mAction.onScrollToClose());
                if (xDiff > yDiff && xDiff > mTouchSlop && hasCanScroll) {
                    mIsBeingDragged = true;
                    mLastMotionX = x;
                    mMoveMotionX = x;
                    mLastMotionY = y;
                    mMoveMotionY = y;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                final int x = (int) ev.getX();
                final int y = (int) ev.getY();
                mLastMotionX = x;
                mMoveMotionX = x;
                mLastMotionY = y;
                mMoveMotionY = y;
                mActivePointerId = ev.getPointerId(0);

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);

                mIsBeingDragged = !mScroller.isFinished();
                Point point = new Point(mMoveMotionX, addYForSDK19(mLastMotionY));
                Rect rect = new Rect();
                mInChild = mAction != null && mAction.inChild(rect, point);

                if (mAction != null && !mIsBeingDragged) {
                    mAction.onTouchDown();
                }
                mDownOver = mLastMotionX <= mScreenWidth / 2;
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mIsBeingDragged = false;
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                mDownOver = false;
                mInChild = false;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }
        return mIsBeingDragged;
    }

    private void onSecondaryPointerUp(MotionEvent ev) {
        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionX = (int) ev.getX(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }

    public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();
        MotionEvent vtev = MotionEvent.obtain(ev);
        final int actionMasked = ev.getActionMasked();

        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
                if (mView instanceof ViewGroup) {
                    if (((ViewGroup) mView).getChildCount() == 0)
                        return false;
                }
                if ((mIsBeingDragged = !mScroller.isFinished())) {
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }

                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }

                mLastMotionX = (int) ev.getX();
                mMoveMotionX = mLastMotionX;

                mLastMotionY = (int) ev.getY();
                mMoveMotionY = mLastMotionY;
                mActivePointerId = ev.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                if (activePointerIndex == -1) {
                    break;
                }

                final int x = (int) ev.getX(activePointerIndex);
                int deltaX = mMoveMotionX - x;
                final int y = (int) ev.getY(activePointerIndex);
                int deltaY = mMoveMotionY - y;
                if (!mIsBeingDragged && Math.abs(deltaX) >= Math.abs(deltaY) && Math.abs(deltaX) > mTouchSlop) {
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    mIsBeingDragged = true;
                    if (deltaX > 0) {
                        deltaX -= mTouchSlop;
                    } else {
                        deltaX += mTouchSlop;
                    }
                }
                if (mIsBeingDragged) {
                    if (mView.getScrollX() + deltaX >= 0) {
                        deltaX = -mView.getScrollX();
                    }
                    mView.scrollBy(deltaX, 0);
                }
                mMoveMotionX = x;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
                    int scrollX = mView.getScrollX();
                    int dx = Math.abs(scrollX);
                    boolean finish = false;
                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                        if (initialVelocity > 0) {
                            finish = true;
                            dx = -(mView.getWidth() - dx);
                        }
                    } else if (Math.abs(mLastMotionX - ev.getX()) > mView.getWidth() / 2) {
                        finish = true;
                        dx = -(mView.getWidth() - dx);
                    }
                    scrollTo(scrollX, dx, finish);
                    mActivePointerId = INVALID_POINTER;
                }
                mDownOver = false;
                mInChild = false;
                break;
            case MotionEvent.ACTION_POINTER_DOWN: {
                final int index = ev.getActionIndex();
                mLastMotionX = (int) ev.getX(index);
                mMoveMotionX = mLastMotionX;
                mActivePointerId = ev.getPointerId(index);
                break;
            }
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                mLastMotionX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
                mMoveMotionX = mLastMotionX;
                break;
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.addMovement(vtev);
        }
        vtev.recycle();
        if (mAction != null) {
            mAction.onScroll();
        }
        return true;
    }

    private void scrollTo(int startX, int dx, boolean finish) {
        if (mAction != null) {
            mAction.startScroll(startX, dx, finish);
        }
    }


    public void setActivityFullScreen(boolean fullScreen) {
        mFullScreen = fullScreen;
    }

    private int addYForSDK19(int y) {
        if (mFullScreen) {
            return y;
        } else {
            return y + mStatusBarHeight;
        }
    }

    private void initOrResetVelocityTracker() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        } else {
            mVelocityTracker.clear();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }


    /**
     * 获取状态栏的高度
     *
     * @param context
     * @return
     */
    public int getStatusBarHeight(Context context) {
        int statusBarHeight = 0;
        try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object o = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = (Integer) field.get(o);
            statusBarHeight = context.getResources().getDimensionPixelSize(x);
        } catch (Exception e) {
            e.printStackTrace();
            if (context instanceof Activity) {
                Rect frame = new Rect();
                ((Activity) context).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
                statusBarHeight = frame.top;
            }
        }
        return statusBarHeight;
    }


}

两个接口类:


public interface SwipeFinishAction extends SwipeFinishTouchCheckAction {

    void startScroll(int startX, int dx, boolean finish);

    void onTouchDown();

    void onScroll();

}

public interface SwipeFinishTouchCheckAction {

    /**
     * 向右滑动是否可关闭activity
     * @return
     */
    boolean onScrollToClose();

    /**
     * 是否点击横向滑动的view
     * @param rect
     * @param point
     * @return
     */
    boolean inChild(Rect rect, Point point);
}

如何使用,已经写在了两个Activity里面了,就是在setContentView之后,执行BaseSwipeFinishActivity里面的addSwipeFinishLayout()方法就行了,还需要把App的主题换一下,然后就可以了。非常之简单。

    <style name="AppTheme" parent="android:Theme.Light">
        //这里是颜色值为#00000000
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:splitMotionEvents">false</item>
        <item name="android:overScrollMode">never</item>
        <item name="android:scrollbars">none</item>
    </style>

附上demo下载地址

如果有大神,有更加好的集成方式,请在下边留言哈…

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值