android 实现倒计时自定view

自定义view实现倒计时 CountDownView

效果如下:

 

 

 

 

 自定义控件java代码:

package com.vpname.vp.widegt;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.Layout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import androidx.annotation.Nullable;

import com.vpname.vp.R;
import com.vpname.vp.listener.OnCallBackListener;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @author zhangwei on 2022/6/28.
 */
public class CountDownView extends View {

    /**
     * 填充背景画笔
     */
    private Paint bgPaint;
    /**
     * 文字画笔
     */
    private TextPaint textPaint;
    /**
     * 分隔号画笔
     */
    private TextPaint semicolonPaint;

    private int frameCount = 3;
    private float bgCenterY;
    /**
     * 输入框的宽高
     */
    private int tvWidthSize = dip2px(80);

    /**
     * 圆角大小
     */
    private int radius = dip2px(0);
    /**
     * 圆角填充颜色
     */
    private int mBgColor = Color.BLACK;
    /**
     * 文字颜色
     */
    private int mTextColor = Color.WHITE;

    /**
     * 文字大小
     */
    private int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 50, getResources().getDisplayMetrics());

    /**
     * 分号颜色
     */
    private int mSemicolonColor = Color.BLACK;

    /**
     * 分号大小
     */
    private int semicolonSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 60, getResources().getDisplayMetrics());


    /**
     * 边框间隔颜色
     */
    private int intervalSize = dip2px(20);

    /**
     * 倒计时时间
     * 单位秒
     */
    private int countDownTime = 60;
    /**
     * 是否默认启动倒计时
     */
    private boolean isAutoPlay = false;
    private OnCallBackListener onCallBackListener;

    public CountDownView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CountDownView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);
    }

    private void initView(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CountDownView);
        intervalSize = typedArray.getDimensionPixelSize(R.styleable.CountDownView_ctv_interval_width, intervalSize);
        radius = typedArray.getDimensionPixelSize(R.styleable.CountDownView_ctv_radius, radius);
        textSize = typedArray.getDimensionPixelSize(R.styleable.CountDownView_ctv_text_size, textSize);
        tvWidthSize = typedArray.getDimensionPixelSize(R.styleable.CountDownView_ctv_text_width, tvWidthSize);
        mTextColor = typedArray.getColor(R.styleable.CountDownView_ctv_text_color, mTextColor);
        semicolonSize = typedArray.getDimensionPixelSize(R.styleable.CountDownView_ctv_text_size, semicolonSize);
        mSemicolonColor = typedArray.getColor(R.styleable.CountDownView_ctv_semicolon_color, mSemicolonColor);
        mBgColor = typedArray.getColor(R.styleable.CountDownView_ctv_bg, mBgColor);
        countDownTime = typedArray.getInt(R.styleable.CountDownView_ctv_time, countDownTime);
        isAutoPlay = typedArray.getBoolean(R.styleable.CountDownView_ctv_auto_play, isAutoPlay);
        typedArray.recycle();
        setBackgroundColor(Color.TRANSPARENT);
        // 增加文本监听器.
        bgPaint = new Paint();
        bgPaint.setStyle(Paint.Style.FILL);
        bgPaint.setColor(mBgColor);

        // 增加文本监听器.
        textPaint = new TextPaint();
        textPaint.setColor(mTextColor);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);

        // 增加文本监听器.
        semicolonPaint = new TextPaint();
        semicolonPaint.setColor(mSemicolonColor);
        semicolonPaint.setAntiAlias(true);
        semicolonPaint.setFakeBoldText(true);
        semicolonPaint.setStrokeWidth(3f);
        semicolonPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        semicolonPaint.setTextSize(semicolonSize);
        if (isAutoPlay) start();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        float bgWidth;
        bgWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
        float bgHeight;
        if (heightMode == MeasureSpec.EXACTLY) {
            bgHeight = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        } else {
            bgHeight = tvWidthSize + dip2px(2);
        }
        bgCenterY = bgHeight / 2;
        setMeasuredDimension((int) bgWidth, (int) bgHeight);
    }


    public void setCountDownTime(int countDownTime) {
        setCountDownTime(countDownTime, false);
    }

    public void setCountDownTime(int countDownTime, boolean isStart) {
        this.countDownTime = countDownTime;
        if (isStart) {
            stop();
            start();
        }
    }


    /**
     * 启动倒计时
     */
    public void start() {
        if (timer == null && countDownTime > 0) {
            timer = new Timer();
            timer.schedule(timerTask, 1000, 1000);
        }
    }

    /**
     * 停止倒计时倒计时
     */
    public void stop() {
        if (timer != null) {
            timerTask.cancel();
            timer.cancel();
            timer = null;
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawStatEndRadiusRect(frameCount, canvas);
        canvas.save();
        drawText(frameCount, canvas);
        canvas.save();
        drawSemicolon(frameCount, canvas);
        canvas.save();
    }

    /**
     * 绘制前后圆角输入框不支持焦点输入框
     */
    private void drawStatEndRadiusRect(int count, Canvas canvas) {
        if (radius == 0) {
            radius = dip2px(5);
        }
        int left = (getWidth() - count * (tvWidthSize) - intervalSize * (count - 1)) / 2;
        for (int i = 0; i < count; i++) {
            RectF rectF = new RectF(left + (tvWidthSize + intervalSize) * i,
                    bgCenterY - tvWidthSize / 2, left + (tvWidthSize + intervalSize) * i + tvWidthSize, bgCenterY + tvWidthSize / 2);
            canvas.drawRoundRect(rectF, radius, radius, bgPaint);
        }
    }


    /**
     * 绘制文字
     */
    private void drawText(int count, Canvas canvas) {
        String[] times = formatTime(countDownTime).split(":");
        int left = (getWidth() - count * (tvWidthSize) - intervalSize * (count - 1)) / 2;
        for (int i = 0; i < count; i++) {
            String text = times[i];
            Rect rect = new Rect();
            textPaint.getTextBounds(text, 0, text.length(), rect);
            float textWidth = Layout.getDesiredWidth(text, textPaint);
            int textHeight = rect.height();
            canvas.drawText(text, left + (tvWidthSize + intervalSize) * i + tvWidthSize / 2 - textWidth / 2, bgCenterY + textHeight / 2, textPaint);
        }
    }


    /**
     * 填充边框颜色
     */
    public void setBgPaintColor(int color) {
        this.bgPaint.setColor(color);
    }

    /**
     * 绘制分号
     */
    private void drawSemicolon(int count, Canvas canvas) {
        int left = (getWidth() - count * (tvWidthSize) - intervalSize * (count - 1)) / 2;
        String text = ":";
        Rect rect = new Rect();
        semicolonPaint.getTextBounds(text, 0, text.length(), rect);
        float textWidth = Layout.getDesiredWidth(text, semicolonPaint);
        int textHeight = rect.height();
        for (int i = 0; i < count - 1; i++) {
            canvas.drawText(text, left + (tvWidthSize + intervalSize) * (i + 1) - intervalSize / 2 - textWidth / 2, bgCenterY + textHeight / 2, semicolonPaint);
        }
    }

    /**
     * 把密度转换为像素
     */
    private int dip2px(float px) {
        final float scale = getScreenDensity();
        return (int) (px * scale + 0.5);
    }

    /**
     * 得到设备的密度
     */
    private float getScreenDensity() {
        return getResources().getDisplayMetrics().density;
    }

    private Timer timer;
    private TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            countDownTime--;
            invalidate();
            if (onCallBackListener != null)
                onCallBackListener.onTick(countDownTime);
            if (countDownTime <= 0) {
                timerTask.cancel();
                timer.cancel();
                timer = null;
            }
        }
    };


    /**
     * 秒转成 00:00:00
     *
     * @param seconds
     * @return
     */
    private String formatTime(long seconds) {
        int temp;
        StringBuffer sb = new StringBuffer();
        if (seconds >= 3600) {
            temp = (int) (seconds / 3600);
            sb.append((seconds / 3600) < 10 ? "0" + temp + ":" : temp + ":");
        } else {
            sb.append("00:");
        }
        temp = (int) (seconds % 3600 / 60);
        changeSeconds(seconds, temp, sb);
        return sb.toString();
    }

    private void changeSeconds(long seconds, int temp, StringBuffer sb) {
        sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");
        temp = (int) (seconds % 3600 % 60);
        sb.append((temp < 10) ? "0" + temp : "" + temp);
    }

    public void setOnCallBackListener(OnCallBackListener onCallBackListener) {
        this.onCallBackListener = onCallBackListener;
    }
}

属性declare-styleable:

 <declare-styleable name="CountDownView">
        <!--倒计时时间-->
        <attr name="ctv_time" format="integer" />
        <!--边框颜色-->
        <attr name="ctv_bg" format="reference|color" />
        <!--边框大小-->
        <attr name="ctv_text_width" format="dimension" />
        <!--文字大小-->
        <attr name="ctv_text_size" format="dimension" />
        <!--间隔-->
        <attr name="ctv_interval_width" format="dimension" />
        <!--圆角-->
        <attr name="ctv_radius" format="dimension" />
        <!--文字颜色-->
        <attr name="ctv_text_color" format="reference|color" />
        <!--分号颜色-->
        <attr name="ctv_semicolon_color" format="reference|color" />
        <!--分号大小-->
        <attr name="ctv_semicolon_size" format="dimension" />
        <!--自动倒计时-->
        <attr name="ctv_auto_play" format="boolean" />
    </declare-styleable>

使用说明:

 <com.vpname.vp.widegt.CountDownView
        android:id="@+id/count_time_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="22dp"
        app:ctv_bg="#FF0051"
        app:ctv_interval_width="29dp"
        app:ctv_radius="10dp"
        app:ctv_text_color="@color/white"
        app:ctv_text_size="51.67sp"
        app:ctv_text_width="62dp"
        app:ctv_time="200"
        app:layout_constraintTop_toBottomOf="@id/hint_tv" />
countDownView.setCountDownTime(countTime);//设置控件的倒计时间

countDownView.binding.countTimeView.setOnCallBackListener(downTime -> {//监听倒计时
    if (downTime == 0) {
       binding.countTimeView.setBgPaintColor(Color.parseColor("#FF0030"));
    }
});
countDownView.start();//启动倒计时
countDownView.stop();//停止倒计时

posted @ 2022-06-29 10:15  别人眼中的过客  阅读(522)  评论(0)    收藏  举报