最近遇到需求,需要滑动退出Activity,参考了大虾们的方案后整理出了两种主流的方法:
- 使用OnTouchEvent,处理触摸事件实现滑动退出
- 使用ViewDragHelper拖动实现滑动退出
两种方法各有利弊,遇到界面上的滑动或滚动事件产生冲突的需要自己处理,下面就来详细的介绍两种实现方法。
0.前提
两种方法不管使用哪一种都需要设置透明主题及Activity中根布局的background,以实现滑动时,上一个Activity可见。
Activity根布局背景:
android:background="?android:colorBackground"
Activity主题:
<style name="Translucent" parent="AppTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>
1.使用OnTouchEvent,处理触摸事件实现滑动退出
先来看看具体实现:
/**
* @author Steven Duan
* @version 1.0
*/
public class SlideLayout extends FrameLayout {
private static final String T = SlideLayout.class.getName();
private Activity mActivity;
private Scroller mScroller;
private int mShadowWidth;
private Drawable mLeftShadow;
private int mLastMoveX;
private int mScreenWidth;
private int mMinX;
public SlideLayout(Activity activity) {
this(activity, null);
}
public SlideLayout(Activity activity, AttributeSet attrs) {
this(activity, attrs, 0);
}
public SlideLayout(Activity activity, AttributeSet attrs, int defStyleAttr) {
super(activity, attrs, defStyleAttr);
Log.d(T, "F初始化 ");
this.mActivity = activity;
mScroller = new Scroller(activity);
//滑动时渐变的阴影
//noinspection deprecation
mLeftShadow = getResources().getDrawable(R.drawable.left_shadow);
//阴影的宽度
mShadowWidth = ((int) getResources().getDisplayMetrics().density) * 16;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMoveX = (int) event.getX();
mScreenWidth = getWidth();
mMinX = mScreenWidth / 10;
break;
case MotionEvent.ACTION_MOVE:
int eventX = (int) event.getX();
Log.d(T, "eventX: " + eventX);
int dx = mLastMoveX - eventX;
if (getScrollX() + dx >= 0) {
scrollTo(0, 0);
} else if (eventX > mMinX) {
//手指处于屏幕边缘时不处理滑动
scrollBy(dx, 0);
}
mLastMoveX = eventX;
break;
case MotionEvent.ACTION_UP:
if (-getScrollX() < mScreenWidth / 2) {
slideBack();
} else {
slideFinish();
}
break;
}
return true;
}
private void slideFinish() {
mScroller.startScroll(getScrollX(), 0, -getScrollX() - mScreenWidth, 0, 200);
invalidate();
}
private void slideBack() {
mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 200);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), 0);
postInvalidate();
} else if (-getScrollX() == mScreenWidth) {
mActivity.finish();
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
//绘制边缘阴影
canvas.save();
//设置阴影的大小范围
mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());
//平移画布
canvas.translate(-mShadowWidth, 0);
//绘制
mLeftShadow.draw(canvas);
canvas.restore();
}
public void bind() {
Log.d(T, "bind: F");
ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
View child = decorView.getChildAt(0);
decorView.removeView(child);
addView(child);
decorView.addView(this);
Log.d(T, "bind成功");
}
}
定义好了容器,那么在Activity中如何使用呢?只需要在Activity的onCreate方法做如下调用即可:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
new SlideLayout(this).bind();
}
代码比较简单,一看便知。只是这种方法在Activity中有滑动或滚动事件会产生冲突,这就涉及事件分发机制,需要自己看情况处理。
2.使用ViewDragHelper拖动实现滑动退出
废话不多说直接上代码:
/**
* @author Steven Duan
* @version 1.0
*/
public class SlideLayout extends FrameLayout {
private View mDragView;
private Activity mActivity;
private ViewDragHelper mViewDragHelper;
private float mSlideWidth;
private int mScreenWidth;
private int mScreenHeight;
private int curSlideX;
public SlideLayout(Context context) {
super(context);
init(context);
}
private void init(Context context) {
mActivity = (Activity) context;
mViewDragHelper = ViewDragHelper.create(this, new DragCallback());
//设置可拖动的边缘
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
/**
* 把DecorView下的子View移除添加到SlideLayout,再将SlideLayout添加到DecorView
*/
public void bind() {
ViewGroup mDecorView = (ViewGroup) mActivity.getWindow().getDecorView();
mDragView = mDecorView.getChildAt(0);
mDecorView.removeView(mDragView);
this.addView(mDragView);
mDecorView.addView(this);
//计算屏幕宽度
DisplayMetrics dm = new DisplayMetrics();
mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
mScreenWidth = dm.widthPixels;
mScreenHeight = dm.heightPixels;
//触发Activity滑出的宽度
mSlideWidth = dm.widthPixels * 0.5f;
}
@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;
invalidate();
if (changedView == mDragView && 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) {
return 0;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
//触发边缘时,主动捕捉mDragView
mViewDragHelper.captureChildView(mDragView, pointerId);
}
}
@Override
public void computeScroll() {
//持续滚动期间,不断重绘
if (mViewDragHelper.continueSettling(true))
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
//进行阴影绘制
canvas.save();
Shader mShader = new LinearGradient(curSlideX - 40, 0, curSlideX, 0,
new int[]{Color.parseColor("#00000000"), Color.parseColor("#50000000")},
null, Shader.TileMode.REPEAT);
//绘制阴影画笔
Paint mPaint = new Paint();
mPaint.setStrokeWidth(2);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.GRAY);
mPaint.setShader(mShader);
RectF rectF = new RectF(curSlideX - 40, 0, curSlideX, mScreenHeight);
canvas.drawRect(rectF, mPaint);
canvas.restore();
}
}
在Activity的onCreate()中调用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forth);
//一句话实现绑定
new SlideLayout(this).bind();
}
小结:两种方法归根结底都是把DecorView下的子View移除添加到SlideLayout,再将SlideLayout添加到DecorView这种思路,只是移动的方式不同而已。再说第二种方法使用ViewDragHelper只能从边缘拖拽,这显得不那么友好。
┗|`O′|┛ 如果你有好的想法思路,欢迎共享讨论。
来源:CSDN
作者:StevenDuan17
链接:https://blog.csdn.net/StevenDuan17/article/details/54948680