侧滑返回效果
2020-05-19 本文已影响0人
菜鸟何时起飞
slideback.gif
使用
//开启滑动返回
SlideBack.create()
.attachToActivity(this);
SlideBack.create()
.slideView(new DefaultSlideView(this))
.attachToActivity(this);
实现原理
1 在DecorView上添加一个充满全屏的FrameLayout
2 在FrameLayout 上添加滑动展示的view
3 通过监听FrameLayout的触摸事件,展现不同的效果
public class DefaultSlideView implements ISlideView {
private Path bezierPath;
private Paint paint, arrowPaint;
//private LinearGradient shader;
private int backViewColor = 0xff000000;
private int arrowColor = Color.WHITE;
private int arrowWidth;
private final int width;
private final int height;
public DefaultSlideView(@NonNull Context context) {
width = Utils.d2p(context, 50);
height = Utils.d2p(context, 200);
arrowWidth = Utils.d2p(context, 4);
init(context);
}
public void setBackViewColor(int backViewColor) {
this.backViewColor = backViewColor;
}
public void setArrowColor(int arrowColor) {
this.arrowColor = arrowColor;
}
private void init(Context context) {
bezierPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(backViewColor);
paint.setStrokeWidth(Utils.d2p(context, 1.5f));
arrowPaint = new Paint();
arrowPaint.setAntiAlias(true);
arrowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
arrowPaint.setColor(arrowColor);
arrowPaint.setStrokeWidth(Utils.d2p(context, 1.5f));
arrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
public boolean scrollVertical() {
return true;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public void onDraw(Canvas canvas, float currentWidth) {
float height = getHeight();
int maxWidth = getWidth();
float centerY = height / 2;
float progress = currentWidth / maxWidth;
if (progress == 0) {
return;
}
paint.setColor(backViewColor);
paint.setAlpha((int) (200 * progress));
//画半弧背景
/*
ps: 小点为起始点和结束点,星号为控制点
·
|
*
*
|
·
|
*
*
|
·
*/
float bezierWidth = currentWidth / 2;
bezierPath.reset();
bezierPath.moveTo(0, 0);
bezierPath.cubicTo(0, height / 4f, bezierWidth, height * 3f / 8, bezierWidth, centerY);
bezierPath.cubicTo(bezierWidth, height * 5f / 8, 0, height * 3f / 4, 0, height);
canvas.drawPath(bezierPath, paint);
arrowPaint.setColor(arrowColor);
arrowPaint.setAlpha((int) (255 * progress));
//画箭头
float arrowLeft = currentWidth / 6;
if (progress <= 0.2) {
//ingore
} else if (progress <= 0.7f) {
//起初变长竖直过程
float newProgress = (progress - 0.2f) / 0.5f;
canvas.drawLine(arrowLeft, centerY - arrowWidth * newProgress, arrowLeft, centerY + arrowWidth * newProgress, arrowPaint);
} else {
//后面变形到完整箭头过程
float arrowEnd = arrowLeft + (arrowWidth * (progress - 0.7f) / 0.3f);
canvas.drawLine(arrowEnd, centerY - arrowWidth, arrowLeft, centerY, arrowPaint);
canvas.drawLine(arrowLeft, centerY, arrowEnd, centerY + arrowWidth, arrowPaint);
}
}
}
public interface ISlideView {
/**
* 是否可以垂直滑动
*
* @return
*/
boolean scrollVertical();
/**
* 宽度
*
* @return
*/
int getWidth();
/**
* 高度
*
* @return
*/
int getHeight();
/**
* 绘制
*
* @param canvas
* @param currentWidth 根据手指滑动得出的当前宽度(最大值为getWidth())
*/
void onDraw(Canvas canvas, float currentWidth);
}
public interface OnSlide {
void onSlideBack();
}
public class SlideBack {
private ISlideView slideView; //样式
private OnSlide onSlide; //滑动监听
private int canSlideWidth; //左边触发距离
public static SlideBack create() {
return new SlideBack();
}
/**
* 滑动返回样式
*
* @param slideView the slide view
* @return the slide back
*/
public SlideBack slideView(ISlideView slideView) {
this.slideView = slideView;
return this;
}
/**
* 左边开始触发距离
*
* @param canSlideWidth the can slide width
* @return the slide back
*/
public SlideBack canSlideWidth(int canSlideWidth) {
this.canSlideWidth = canSlideWidth;
return this;
}
/**
* 滑动触发监听
*
* @param onSlide the on slide
* @return the slide back
*/
public SlideBack onSlide(OnSlide onSlide) {
this.onSlide = onSlide;
return this;
}
public SlideControlLayout attachToActivity(@NonNull Activity activity) {
if (slideView == null) {
slideView = new DefaultSlideView(activity);
}
if (canSlideWidth == 0) {
canSlideWidth = Utils.d2p(activity, 18);
}
return new SlideControlLayout(activity, canSlideWidth, slideView, onSlide)
.attachToActivity(activity);
}
}
class SlideBackView extends View {
private ISlideView slideView;
private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private ValueAnimator animator;
private float rate = 0;//曲线的控制点
SlideBackView(Context context, @NonNull ISlideView slideView) {
super(context);
setSlideView(slideView);
}
public SlideBackView setSlideView(@NonNull ISlideView slideView) {
this.slideView = slideView;
setLayoutParams(new SlideControlLayout.LayoutParams(slideView.getWidth(), slideView.getHeight()));
return this;
}
public ISlideView getSlideView() {
return slideView;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
slideView.onDraw(canvas, rate);
}
public void updateRate(float updateRate, boolean hasAnim) {
if (updateRate > getWidth()) {
updateRate = getWidth();
}
if (rate == updateRate) {
return;
}
cancelAnim();
if (!hasAnim) {
rate = updateRate;
invalidate();
if (rate == 0) {
setVisibility(GONE);
}else{
setVisibility(VISIBLE);
}
}
/*这个动画效果并不明显*/
animator = ValueAnimator.ofFloat(rate, updateRate);
animator.setDuration(200);
animator.addUpdateListener(animation -> {
rate = (Float) animation.getAnimatedValue();
postInvalidate();
if (rate == 0) {
setVisibility(GONE);
}else{
setVisibility(VISIBLE);
}
});
animator.setInterpolator(DECELERATE_INTERPOLATOR);
animator.start();
}
private void cancelAnim() {
if (animator != null && animator.isRunning()) {
animator.cancel();
}
}
@Override
protected void onDetachedFromWindow() {
cancelAnim();
if (rate != 0) {
rate = 0;
invalidate();
}
super.onDetachedFromWindow();
}
}
public class SlideControlLayout extends FrameLayout {
private final SlideBackView slideBackView;
private final OnSlide onSlide;
private int canSlideWidth;
private boolean enable = true;
private float downX;
private float moveX;
private boolean startDrag = false;
SlideControlLayout(@NonNull Context context, int canSlideWidth, ISlideView slideView, OnSlide onSlide) {
super(context);
this.canSlideWidth = canSlideWidth;
this.onSlide = onSlide;
slideBackView = new SlideBackView(context, slideView);
addView(slideBackView);
}
SlideControlLayout attachToActivity(@NonNull Activity activity) {
ViewParent parent = getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(this);
}
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
decor.addView(this, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return this;
}
private void onBack() {
if (onSlide == null) {
Utils.getActivityContext(getContext()).onBackPressed();
} else {
onSlide.onSlideBack();
}
}
private void setSlideViewY(SlideBackView view, int y) {
if (!view.getSlideView().scrollVertical()) {
/*水平移动到0 0 点*/
scrollTo(0, 0);
return;
}
/*移动的是视图内容 移动到垂直居中*/
scrollTo(0, -(y - view.getHeight() / 2));
}
//region 手势控制
@Override
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
if (!enable) {
return false;
}
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
if (motionEvent.getRawX() <= canSlideWidth) {
return true;
}
}
return super.onInterceptTouchEvent(motionEvent);
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
if (!enable) {
return super.onTouchEvent(motionEvent);
}
float currentX = motionEvent.getRawX();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
float currentY = motionEvent.getRawY();
if (currentY > Utils.d2p(getContext(), 100) && currentX <= canSlideWidth) {
downX = currentX;
startDrag = true;
slideBackView.updateRate(0, false);
setSlideViewY(slideBackView, (int) (motionEvent.getRawY()));
}
break;
case MotionEvent.ACTION_MOVE:
if (startDrag) {
moveX = currentX - downX;
if (Math.abs(moveX) <= slideBackView.getWidth() * 2) {
slideBackView.updateRate(Math.abs(moveX) / 2, false);
} else {
slideBackView.updateRate(slideBackView.getWidth(), false);
}
/*移动view*/
setSlideViewY(slideBackView, (int) (motionEvent.getRawY()));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
if (startDrag && moveX >= slideBackView.getWidth() * 2) {
onBack();
slideBackView.updateRate(0, false);
} else {
slideBackView.updateRate(0, startDrag);
}
moveX = 0;
startDrag = false;
break;
}
return startDrag || super.onTouchEvent(motionEvent);
}
//endregion
}
class Utils {
@ColorInt
static int setColorAlpha(int color, float alpha) {
color = Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color));
return color;
}
static int d2p(Context var0, float var1) {
DisplayMetrics var2 = var0.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(1, var1, var2);
}
/**
* 屏幕宽度(像素)
*/
private static int screentwidth;
static int getScreenWidth(Context context) {
if (screentwidth > 0)
return screentwidth;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return screentwidth = outMetrics.widthPixels;
}
static Activity getActivityContext(Context context) {
if (context == null)
return null;
else if (context instanceof Activity)
return (Activity) context;
else if (context instanceof ContextWrapper)
return getActivityContext(((ContextWrapper) context).getBaseContext());
return null;
}
}