侧滑返回效果

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;
    }
}
上一篇下一篇

猜你喜欢

热点阅读