转载请注明出处:meijian531161724
常常可以看到,很多Android应用都有这么一个功能,就是滑动关闭Activity,比如微信,CSDN移动端,百度贴吧移动端等。我自己也想写个滑动关闭Activity,最近事情没有那么多,我就google了一下,查看了一下实现滑动关闭Activity的实现方法,其中,有个思路,我觉得很不错,因此,在这里,我通过别人的思路,自己实现了一下滑动关闭Activity的方法,在此记录一下。
首先我们先看下实现效果:
要写滑动关闭Activity,有几个问题要解决:
1.透明的显示底层的Activity。
2.边界检测,滑动视图,以及自动滚动。
3.阴影绘制。
一、透明的显示底层Activity,可以使用透明主题,也可以使用其他主题,但是必须修改主题的几个属性,来达到透明的效果,如:
[html] view plain copy- <style name="AppTheme" parent="@style/Theme.AppCompat.Light">
- <item name="android:windowBackground">@android:color/transparent</item>
- </style>
二、谷歌在V4包中,增加ViewDragHelper类,这个类能够对滑动,边界检测,自动滚动等功能,提供了很好的实现。因此在这里我们选择ViewDragHelper来实现滑动功能。
三、阴影绘制,Paint画笔来绘制。我们选择在dispatchDraw()方法中绘制,为什么不用onDraw(),因为onDraw有时候在ViewGroup中不会执行。我们使用画笔的setShader(),通过写一个LinearGradient(),再绘制一个矩形,得到阴影效果。
最核心的原理就是在于,替换Window的DecorView下的LinearLayout。下面从代码直观的说明:
[java] view plain copy- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.BitmapShader;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.LinearGradient;
- import android.graphics.Paint;
- import android.graphics.RectF;
- import android.graphics.Shader;
- import android.support.v4.widget.ViewDragHelper;
- import android.util.DisplayMetrics;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.FrameLayout;
- import java.util.Map;
- /**
- * Created by FengJun on 2016/12/21.
- * 功能:当activity布局中嵌入当前布局,该activity可以从边缘滑动关闭
- * 实现原理:
- * 1.获取DecorView的RootView,删除RootView,把RootView添加到当前View
- * 再把当前View添加到DecorView
- */
- public class SlideBackLayout extends FrameLayout {
- /**当前Activity的DecorView*/
- private ViewGroup mDecorView;
- /**DecorView下的LinearLayout*/
- private View mRootView;
- /**需要边缘滑动删除的Activity*/
- private Activity mActivity;
- /**Drag助手类*/
- private ViewDragHelper mViewDragHelper;
- /**触发退出当前Activity的宽度*/
- private float mSlideWidth;
- /**屏幕的宽和高*/
- private int mScreenWidth;
- private int mScreenHeight;
- /**画笔,用来绘制阴影效果*/
- private Paint mPaint;
- /**用于记录当前滑动距离*/
- private int curSlideX;
- public SlideBackLayout(Context context) {
- super(context);
- init(context);
- }
- private void init(Context context) {
- //必须是传入Activity
- mActivity = (Activity) context;
- //构造ViewDragHelper
- mViewDragHelper = ViewDragHelper.create(this, new DragCallback());
- //设置从左边缘捕捉View
- mViewDragHelper.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 mViewDragHelper.shouldInterceptTouchEvent(event);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- mViewDragHelper.processTouchEvent(event);
- return true;
- }
- class DragCallback 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) {
- //缓慢滑动的方法,小于触发条件,滚回去
- mViewDragHelper.settleCapturedViewAt(0, 0);
- } else {
- //大于触发条件,滚出去...
- mViewDragHelper.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();
- }
- }
- @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
- mViewDragHelper.captureChildView(mRootView, pointerId);
- }
- }
- @Override
- public void computeScroll() {
- //使用settleCapturedViewAt方法是,必须重写computeScroll方法,传入true
- //持续滚动期间,不断刷新ViewGroup
- if (mViewDragHelper.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("#1edddddd"), Color.parseColor("#6e666666"), Color.parseColor("#9e666666")}, null, Shader.TileMode.REPEAT);
- //设置着色器
- mPaint.setShader(mShader);
- //绘制时,注意向左边偏移
- RectF rectF = new RectF(curSlideX - 40, 0, curSlideX, mScreenHeight);
- canvas.drawRect(rectF, mPaint);
- canvas.restore();
- }
- }
我在代码中,进行详细的注释。总体来说,不难理解。我们在使用的时候,在布局文件中,一定要在根布局设置背景颜色,否则整个布局将会是透明的。下面是使用方法:
[java] view plain copy
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.widget.TextView;
- import com.mjc.slidebackdemo.view.SlideBackLayout;
- /**
- * 从左侧边缘向右滑动可以关闭当前页面
- */
- public class SecondActivity extends AppCompatActivity {
- private TextView tv;
- private SlideBackLayout mSlideBackLayout;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mSlideBackLayout = new SlideBackLayout(this);
- mSlideBackLayout.bind();
- tv = (TextView) findViewById(R.id.tv);
- }
- }
哪个Activity需要滑动关闭的功能,只需要实例化一个SlideBackLayout对象,并调用bind()方法。这样,我们就实现了滑动关闭的效果了。
项目地址:https://github.com/AndStuFeng/SlideBackDemo建议API 22以上