按钮长按结束,实时进度动画

很多跑步软件,暂停后的结束按钮都有一个这样的动画:

长按“结束”按钮,才能结束跑步,长按时候,伴随一个实时的进度动画,实际效果图如下(真机运行流畅,gif中会卡顿):



实现思路:

1、处理点击事件,保证长按时,动画开始;长按结束(手指移动、手指抬起)动画结束

2、进度动画实现

因为是协作开发提供给其他开发者使用,所以用自定义组件的来实现了,还要有结束接口暴露出去。

下面直接贴代码了,实现也比较简单,希望能帮助到有需要的人。

public class LongPressToFinishButton extends RelativeLayout {
    private int mLastMotionX, mLastMotionY;
    private static final int TOUCH_SLOP = 20;
    private final int DURATION = 1000;
    int roundWidth;//圆环宽度
    private boolean isMoved = false;
    private int progress = 0;
    private Paint backgroundCirclePaint, progressCirclePaint;
    private ValueAnimator valueAnimator;
    private boolean isPress;
    RelativeLayout buttonLayout;

    public LongPressToFinishButton(Context context) {
        super(context);
        init();
    }

    public LongPressToFinishButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LongPressToFinishButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View convertView = inflater.inflate(R.layout.long_press_to_finish_button, this, true);
        buttonLayout = (RelativeLayout) convertView.findViewById(R.id.button_layout);
        roundWidth = DisplayUtils.dip2px(getContext(), 3);
        backgroundCirclePaint = new Paint();
        backgroundCirclePaint.setStyle(Paint.Style.STROKE);
        backgroundCirclePaint.setColor(Color.parseColor("#565351"));
        backgroundCirclePaint.setAntiAlias(true);
        backgroundCirclePaint.setStrokeWidth(roundWidth);
        progressCirclePaint = new Paint();
        progressCirclePaint.setStyle(Paint.Style.STROKE);
        progressCirclePaint.setColor(Color.parseColor("#E8A387"));
        progressCirclePaint.setAntiAlias(true);
        progressCirclePaint.setStrokeWidth(roundWidth);
        valueAnimator = ValueAnimator.ofInt(0, 100);
        valueAnimator.setDuration(DURATION);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                progress = (int) valueAnimator.getAnimatedValue();
                postInvalidate();
                if (progress == 100) {
                    postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            isPress = false;
                            postInvalidate();
                            if (onFinishListener != null) {
                                onFinishListener.onFinish();
                            }
                        }
                    }, 50);
                }
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isPress = false;
                postInvalidate();
            }
        });
    }


    private OnFinishListener onFinishListener;

    public interface OnFinishListener {
        void onFinish();
    }

    public void setOnFinishListener(OnFinishListener onFinishListener) {
        this.onFinishListener = onFinishListener;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        Log.i("nieqi", "dispatchDraw , progress = " + progress);
        int center = getWidth() / 2;
        int radius = center - roundWidth / 2; //圆环的半径
        canvas.save();
        RectF oval = new RectF(center - radius, center - radius, center
                + radius, center + radius);
        if (isPress) {
            canvas.drawArc(oval, 0, 360, false, backgroundCirclePaint);
            canvas.drawArc(oval, -90, 360 * progress / 100, false, progressCirclePaint);
        }
        canvas.restore();
    }

    private void startAnim() {
        Log.i("nieqi", "startAnimation");
        if (valueAnimator != null) {
            valueAnimator.start();
        }
    }

    private void cancelAnimation() {
        isPress = false;
        if (valueAnimator != null) {
            valueAnimator.cancel();
        }
        progress = 0;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (x >= buttonLayout.getLeft() && x <= buttonLayout.getRight()
                        && y >= buttonLayout.getTop() && y <= buttonLayout.getBottom()) {//
                    Log.i("nieqi", "press");
                    mLastMotionX = x;
                    mLastMotionY = y;
                    isMoved = false;
                    isPress = true;
                    startAnim();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isMoved) {
                    break;
                }
                if (Math.abs(mLastMotionX - x) > TOUCH_SLOP
                        || Math.abs(mLastMotionY - y) > TOUCH_SLOP) {
                    isMoved = true;
                    cancelAnimation();
                }
                break;
            case MotionEvent.ACTION_UP:
                cancelAnimation();
                break;
        }
        return true;
    }
}


重写dispatchTouchEvent 

是为了监听长按,因为系统提供的setOnLongClickListener无法满足要求

代码中,当我们接收到ACTION_DOWN时,判断是否是在圆形按钮范围内,接下来开启长按的状态,开启动画

重写dispatchDraw

一般我们自定义View中,都是重写draw或者onDraw方法,但是这里必须要去重写dispatchDraw,不然postInvalidate是没有办法刷新界面的

当时就犯了这个错误,卡了很久。

因为我们继承的是ViewGroup,网上的说法是:

自定义一个ViewGroup,重写onDraw。

     onDraw可能不会被调用,原因是需要先设置一个背景(颜色或图)。

           表示这个group有东西需要绘制了,才会触发draw,之后是onDraw

    因此,一般直接重写dispatchDraw来绘制viewGroup


所以就只需要处理好这两点,就能请轻松的实现了。

Good luck!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值