自定义圆环倒计时

本文介绍了一个自定义的圆环倒计时组件`CountDownView`的实现,包括颜色、尺寸等属性的自定义,以及利用属性动画和`CountDownTimer`进行倒计时效果的展示。此外,还展示了如何将圆球从屏幕一角移动到另一角的动画效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class CountDownView extends View{

    private static int defaultCircleSolideColor = Color.BLUE;
    private static int defaultCircleStrokeColor = Color.WHITE;//最底层的颜色
    private static int defaultCircleStrokeWidth = 10;
    private static int defaultCircleRadius = 60;
    private static int progressColor = Color.YELLOW;//进度条的颜色
    private static int progressWidth = 11;//>defaultCircleStrokeWidth
    private static int smallCircleSolideColor = Color.BLACK;
    private static int smallCircleStrokeColor = Color.WHITE;
    private static float smallCircleStrokeWidth = 8;
    private static float smallCircleRadius = 30;
    private static int textColor = Color.WHITE;//BLACK
    private static float textSize = 30;
    private static Paint defaultCriclePaint;
    private static Paint progressPaint;
    private static Paint textPaint;
    private static float currentAngle;
    public static String textDesc;
    public static long countdownTime;
    private static int mStartSweepValue = -90;

    //设置画布圆形背景
    private Paint backgroundPaint;
    public static ValueAnimator animator;

    public CountDownView(Context context) {
        super(context);
    }

    public CountDownView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initStyle(attrs);
        setPaint();
    }

    public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性
        initStyle(attrs);
        setPaint();
    }

    private void initStyle(AttributeSet attrs){
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CountDownProgress);
        int indexCount = typedArray.getIndexCount();
        for(int i=0;i<indexCount;i++){
            int attr = typedArray.getIndex(i);
            switch (attr){
                case R.styleable.CountDownProgress_default_circle_solide_color:
                    defaultCircleSolideColor = typedArray.getColor(attr, defaultCircleSolideColor);
                    break;
                case R.styleable.CountDownProgress_default_circle_stroke_color:
                    defaultCircleStrokeColor = typedArray.getColor(attr, defaultCircleStrokeColor);
                    break;
                case R.styleable.CountDownProgress_default_circle_stroke_width:
                    defaultCircleStrokeWidth = (int) typedArray.getDimension(attr, defaultCircleStrokeWidth);
                    break;
                case R.styleable.CountDownProgress_default_circle_radius:
                    defaultCircleRadius = (int) typedArray.getDimension(attr, defaultCircleRadius);
                    break;
                case R.styleable.CountDownProgress_progress_color:
                    progressColor = typedArray.getColor(attr, progressColor);
                    break;
                case R.styleable.CountDownProgress_progress_width:
                    progressWidth = (int) typedArray.getDimension(attr, progressWidth);
                    break;
                case R.styleable.CountDownProgress_small_circle_solide_color:
                    smallCircleSolideColor = typedArray.getColor(attr, smallCircleSolideColor);
                    break;
                case R.styleable.CountDownProgress_small_circle_stroke_color:
                    smallCircleStrokeColor = typedArray.getColor(attr, smallCircleStrokeColor);
                    break;
                case R.styleable.CountDownProgress_small_circle_stroke_width:
                    smallCircleStrokeWidth = (int) typedArray.getDimension(attr, smallCircleStrokeWidth);
                    break;
                case R.styleable.CountDownProgress_small_circle_radius:
                    smallCircleRadius = (int) typedArray.getDimension(attr, smallCircleRadius);
                    break;
                case R.styleable.CountDownProgress_text_color:
                    textColor = typedArray.getColor(attr, textColor);
                    break;
                case R.styleable.CountDownProgress_text_size:
                    textSize = (int) typedArray.getDimension(attr, textSize);
                    break;
            }
        }
        typedArray.recycle();

    }

    private void setPaint() {
        //默认圆
        defaultCriclePaint = new Paint();
        defaultCriclePaint.setAntiAlias(true);//抗锯齿
        defaultCriclePaint.setDither(true);//防抖动
        defaultCriclePaint.setStyle(Paint.Style.STROKE);
        defaultCriclePaint.setStrokeWidth(defaultCircleStrokeWidth);
        defaultCriclePaint.setColor(defaultCircleStrokeColor);//这里先画边框的颜色,后续再添加画笔画实心的颜色
        //默认圆上面的进度弧度
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setDither(true);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(progressWidth);
        progressPaint.setColor(progressColor);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔笔刷样式

        //文字画笔
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setColor(textColor);
        textPaint.setTextSize(textSize);

        backgroundPaint=new Paint();
        backgroundPaint.setAntiAlias(true);//抗锯齿
        backgroundPaint.setDither(true);//防抖动
        backgroundPaint.setStyle(Paint.Style.FILL);
        backgroundPaint.setStrokeWidth(defaultCircleStrokeWidth);
        backgroundPaint.setColor(progressColor);//这里先画边框的颜色,后续再添加画笔画实心的颜色

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.translate(getPaddingLeft(), getPaddingTop());
        //背景圆
        canvas.drawCircle(defaultCircleRadius, defaultCircleRadius, defaultCircleRadius, backgroundPaint);

        //画默认圆
        canvas.drawCircle(defaultCircleRadius, defaultCircleRadius, defaultCircleRadius, defaultCriclePaint);
        //画进度圆弧
        //currentAngle = getProgress()*1.0f/getMax()*360;recf适用于划出一块绘制的区域mStartSweepValue是开始的位置
        canvas.drawArc(new RectF(0, 0, defaultCircleRadius*2, defaultCircleRadius*2),mStartSweepValue, 360*currentAngle,false,progressPaint);
        //画中间文字
        //   String text = getProgress()+"%";
        //获取文字的长度的方法
        float textWidth = textPaint.measureText(textDesc);
        float textHeight = (textPaint.descent() + textPaint.ascent()) / 2;
        canvas.drawText(textDesc, defaultCircleRadius - textWidth/2, defaultCircleRadius - textHeight, textPaint);

        canvas.restore();

    }

    /**
     * 如果该View布局的宽高开发者没有精确的告诉,则需要进行测量,如果给出了精确的宽高则我们就不管了
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize;
        int heightSize;
        int strokeWidth = Math.max(defaultCircleStrokeWidth, progressWidth);
        //精确指定宽高
        if(widthMode != MeasureSpec.EXACTLY){
            widthSize = getPaddingLeft() + defaultCircleRadius*2 + strokeWidth + getPaddingRight();
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }
        if(heightMode != MeasureSpec.EXACTLY){
            heightSize = getPaddingTop() + defaultCircleRadius*2 + strokeWidth + getPaddingBottom();
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    //属性动画
    public void startCountDownTime(final OnCountdownFinishListener countdownFinishListener){
        setClickable(true);
        animator = ValueAnimator.ofFloat(0, 1.0f);
        //动画时长,让进度条在CountDown时间内正好从0-360走完,这里由于用的是CountDownTimer定时器,倒计时要想减到0则总时长需要多加1000毫秒,所以这里时间也跟着+1000ms
        animator.setDuration(countdownTime );//+ 1000
        animator.setInterpolator(new LinearInterpolator());//匀速
        animator.setRepeatCount(0);//表示不循环,-1表示无限循环
        //值从0-1.0F 的动画,动画时长为countdownTime,ValueAnimator没有跟任何的控件相关联,那也正好说明ValueAnimator只是对值做动画运算,而不是针对控件的,我们需要监听ValueAnimator的动画过程来自己对控件做操作
        //添加监听器,监听动画过程中值的实时变化(animation.getAnimatedValue()得到的值就是0-1.0)
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                /**
                 * 这里我们已经知道ValueAnimator只是对值做动画运算,而不是针对控件的,因为我们设置的区间值为0-1.0f
                 * 所以animation.getAnimatedValue()得到的值也是在[0.0-1.0]区间,而我们在画进度条弧度时,设置的当前角度为360*currentAngle,
                 * 因此,当我们的区间值变为1.0的时候弧度刚好转了360度
                 */
                currentAngle = (float) animation.getAnimatedValue();
                //       Log.e("currentAngle",currentAngle+"");
                invalidate();//实时刷新view,这样我们的进度条弧度就动起来了
            }
        });
        //开启动画
        animator.start();
        //还需要另一个监听,监听动画状态的监听器
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //倒计时结束的时候,需要通过自定义接口通知UI去处理其他业务逻辑
                if(countdownFinishListener != null){
                    countdownFinishListener.countdownFinished();
                }
                if(countdownTime > 0){
                    setClickable(true);
                }else{
                    setClickable(false);
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        //调用倒计时操作
        countdownMethod();
    }

    public CountDownTimer timer;

    //倒计时的方法
    private void countdownMethod(){
        timer= new CountDownTimer(countdownTime+1000, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                //         Log.e("time",countdownTime+"");
                countdownTime = countdownTime-1000;
                textDesc = "跳过("+((countdownTime/1000)) + ")";

                //countdownTime = countdownTime-1000;
                //刷新view
                invalidate();
            }
            @Override
            public void onFinish() {
                //textDesc = 0 + "″";
                //刷新view
//                textDesc="跳过(0)";
//                invalidate();
            }
        }.start();
    }
    public void setCountdownTime(long countdownTime){
        this.countdownTime = countdownTime;
        textDesc = countdownTime / 1000 + "″";
    }

    public interface OnCountdownFinishListener{
        void countdownFinished();
    }


}













 
<com.bawei.macbook.moni.persenter.CountDownView
    android:id="@+id/countdwonview"
    android:layout_width="90dp"
    android:layout_height="100dp"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingTop="35dp"
    android:paddingRight="10dp"
    android:paddingLeft="10dp"


    />








//////////////////////////////////////////////////////////////////////////////////////// 
//设置自定义圆球在左上角移到右下角


public class MainActivity extends AppCompatActivity {

    private CustomView2 yuan;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         Display My_Display = getWindow().getWindowManager().getDefaultDisplay();
        int Max_X = My_Display.getWidth();
        int Max_Y = My_Display.getHeight();
        yuan = (CustomView2) findViewById(R.id.yuan);


        ObjectAnimator animator = ObjectAnimator.ofFloat(yuan, "translationX", Max_X);
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(yuan, "translationY", Max_Y);

        AnimatorSet set = new AnimatorSet();
        set.setDuration(4000);

        set.play(animator).with(animator1);//a1,a2同时执行,之后执行a3

        set.start();


        set.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {


                Intent intent = new Intent(MainActivity.this, MainActivity2.class);
                startActivity(intent);

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }

        });


    }


}


//定义圆球
public class CustomView2 extends View{



    public CustomView2(Context context) {
        super(context);
    }


    public CustomView2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Paint paint=new Paint();
        paint.setColor(Color.YELLOW);
        paint.setStyle(Paint.Style.FILL);
       // paint.setAntiAlias(true);
        // 画圆形
        canvas.drawCircle(100, 100, 100, paint);


    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值