轻松实现右滑关闭当前Activity

常常可以看到,很多Android应用都有这么一个功能,就是滑动关闭Activity,比如微信,CSDN移动端,百度贴吧移动端等。我自己也想写个滑动关闭Activity,最近事情没有那么多,我就google了一下,查看了一下实现滑动关闭Activity的实现方法,其中,有个思路,我觉得很不错,因此,在这里,我通过别人的思路,自己实现了一下滑动关闭Activity的方法,在此记录一下。

首先我们先看下实现效果:

要写滑动关闭Activity,有几个问题要解决:

  • 1.透明的显示底层的Activity。
  • 2.边界检测,滑动视图,以及自动滚动。
  • 3.阴影绘制。

透明的显示底层Activity,可以使用透明主题,也可以使用其他主题,但是必须修改主题的几个属性,来达到透明的效果,如:

<style name="SwipeBackStyle" parent="AppTheme">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
</style>

谷歌在V4包中,增加ViewDragHelper类,这个类能够对滑动,边界检测,自动滚动等功能,提供了很好的实现。因此在这里我们选择ViewDragHelper来实现滑动功能。

阴影绘制,Paint画笔来绘制。我们选择在dispatchDraw()方法中绘制,为什么不用onDraw(),因为onDraw有时候在ViewGroup中不会执行。我们使用画笔的setShader(),通过写一个LinearGradient(),再绘制一个矩形,得到阴影效果。

最核心的原理就是在于,替换Window的DecorView下的LinearLayout。下面从代码直观的说明:

public class SwipeBackLayout extends FrameLayout {

    //当前Activity的DecorView
    private ViewGroup mDecorView;
    //DecorView下的LinearLayout
    private View mRootView;
    // Activity
    private Activity mActivity;

    private ViewDragHelper mDragHelper;

    //触发退出当前Activity的宽度
    private float mSlideWidth;
    //屏幕的宽和高
    private int mScreenWidth;
    private int mScreenHeight;
    //画笔,用来绘制阴影效果
    private Paint mPaint;
    //用于记录当前滑动距离
    private int curSlideX;

    public SwipeBackLayout(@NonNull Context context) {
        super(context);

        init(context);
    }

    private void init(Context context) {
        //必须是传入Activity
        mActivity = (Activity) context;

        mDragHelper = ViewDragHelper.create(this, new SwipeBackDragCallback());
        // 设置从左边缘捕获view
        mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);

        //初始化画笔
        mPaint = new Paint();
        mPaint.setStrokeWidth(2);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.GRAY);
    }

    //绑定方法,在Activity的DecorView下插入当前ViewGroup,原来的RootView放于当前ViewGroup下
    public void bind() {
        mDecorView = (ViewGroup) mActivity.getWindow().getDecorView();
        mRootView = mDecorView.getChildAt(0);
        mDecorView.removeView(mRootView);
        this.addView(mRootView);
        mDecorView.addView(this);

        //计算屏幕宽度
        DisplayMetrics dm = new DisplayMetrics();
        mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        mScreenWidth = dm.widthPixels;
        mScreenHeight = dm.heightPixels;
        mSlideWidth = dm.widthPixels * 0.28f;
    }

    @Override
    public boolean onInterceptHoverEvent(MotionEvent event) {
        return mDragHelper.shouldInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        //使用settleCapturedViewAt方法是,必须重写computeScroll方法,传入true
        //持续滚动期间,不断刷新ViewGroup
        if (mDragHelper.continueSettling(true))
            invalidate();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        //进行阴影绘制,onDraw()方法在ViewGroup中不一定会执行
        drawShadow(canvas);
        super.dispatchDraw(canvas);

    }


    private void drawShadow(Canvas canvas) {
        canvas.save();
        //构造一个渐变
        Shader mShader = new LinearGradient(curSlideX - 40, 0, curSlideX, 0, new int[]{Color.parseColor("#00dddddd"), Color.parseColor("#33666666"), Color.parseColor("#90666666")}, null, Shader.TileMode.REPEAT);
        //设置着色器
        mPaint.setShader(mShader);
        //绘制时,注意向左边偏移
        RectF rectF = new RectF(curSlideX - 40, 0, curSlideX, mScreenHeight);
        canvas.drawRect(rectF, mPaint);
        canvas.restore();
    }

    class SwipeBackDragCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return false;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //当前回调,松开手时触发,比较触发条件和当前的滑动距离
            int left = releasedChild.getLeft();
            if (left <= mSlideWidth) {
                //缓慢滑动的方法,小于触发条件,滚回去
                mDragHelper.settleCapturedViewAt(0, 0);
            } else {
                //大于触发条件,滚出去...
                mDragHelper.settleCapturedViewAt(mScreenWidth, 0);
            }
            //需要手动调用更新界面的方法
            invalidate();
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            curSlideX = left;
            //当滑动位置改变时,刷新View,绘制新的阴影位置
            invalidate();
            //当滚动位置到达屏幕最右边,则关掉Activity
            if (changedView == mRootView && left >= mScreenWidth) {
                mActivity.finish();
                mActivity.overridePendingTransition(0, 0);
            }
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //限制左右拖拽的位移
            left = left >= 0 ? left : 0;
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //上下不能移动,返回0
            return 0;
        }

        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            //触发边缘时,主动捕捉mRootView
            mDragHelper.captureChildView(mRootView, pointerId);
        }
    }
}

我在代码中,进行详细的注释。总体来说,不难理解。我们在使用的时候,在布局文件中,一定要在根布局设置背景颜色,否则整个布局将会是透明的。下面是使用方法:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_swipe_back);
    SwipeBackLayout mSwipeBackLayout = new SwipeBackLayout(this);
    mSwipeBackLayout.bind();
}

原文发布于微信公众号 - Android机动车(JsAndroidClub)

原文发表时间:2018-07-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货

安卓开发_浅谈Android动画(三)

18240
来自专栏Jack的Android之旅

模仿企鹅FM播放主页面滑动动态改变各视图的大小

国庆的一个任务就是把自己之前写的代码搬到博客。这次给各位带来的是通过滑动来动态改变各个View的大小进而达到企鹅FM播放页面的滑动效果(仅仅是滑动效果),老规矩...

10820
来自专栏Android小菜鸡

Android 简单实现控件滑动固定效果

  首先我们需要实时的获取滑动的Y值scrollDistanceY(可以理解为滑动了的距离),可以通过ScrollView的getScrollY();方法来获取...

29730
来自专栏郭霖

Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效

之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章 Android滑动菜单特...

34760
来自专栏Android开发指南

Scrollview回弹效果自定义控件

33050
来自专栏向治洪

android自定义view实现progressbar的效果

一键清理是很多Launcher都会带有的功能,其效果也比较美观。实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:模仿实...

29250
来自专栏Android干货

Android项目实战(三十二):圆角对话框Dialog

40060
来自专栏小巫技术博客

A020-列表容器之ListView

前面介绍了Android UI中的五大布局容器,本节课介绍实际项目当中经常会用到的组件-ListView,它也是一个布局容器,它的每一项就是我们的列表项,每一个...

11130
来自专栏郭霖

Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个朋友在评论中留言,希望我可以帮他将这个滑动菜单改成双向滑动的方式。当时也没想花太多时间,简...

41360
来自专栏7号代码

Android应用界面开发——ListView,GridView,ScrollView

ListView的意思是列表视图,是应用最广泛的一种视图,例如联系人,功能列表,菜单等等都会用到ListView。

11430

扫码关注云+社区

领取腾讯云代金券