`

CountDownDigitalClock:倒计时的TextView

阅读更多
http://www.cnblogs.com/over140/archive/2010/08/27/1809873.html

改了一下,但是输出格式未能实现自定义,原因在于下面的代码中显示时间差不正确,我不知道什么原因。

mFormat = "距离结束还有dd天kk小时mm分ss秒";//yyyy-MM-dd hh:mm:ss
mCalendar.setTimeInMillis(mTimeDistance);//为什么这样计算的时间不对???
setText(DateFormat.format(mFormat, mCalendar));




我只能退一步,将就着了,源码是这样的:
import java.util.Calendar;

import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;

public class CountDownDigitalClock extends android.widget.DigitalClock {

	Calendar mCalendar;
	private final static String m12 = "h:mm aa";// h:mm:ss aa
	private final static String m24 = "k:mm";// k:mm:ss
	private FormatChangeObserver mFormatChangeObserver;

	private Runnable mTicker;
	private Handler mHandler;

	private boolean mTickerStopped = false;

	String mFormat;

	private long mDeadTime;
	private OnCountDownListener onCountDownListener;

	public CountDownDigitalClock(Context context) {
		super(context);
		initClock(context);
	}

	public CountDownDigitalClock(Context context, AttributeSet attrs) {
		super(context, attrs);
		initClock(context);
	}

	private void initClock(Context context) {

		if (mCalendar == null) {
			mCalendar = Calendar.getInstance();
		}

		mFormatChangeObserver = new FormatChangeObserver();
		getContext().getContentResolver().registerContentObserver(
				Settings.System.CONTENT_URI, true, mFormatChangeObserver);

		setFormat();
	}

	@Override
	protected void onAttachedToWindow() {
		mTickerStopped = false;
		super.onAttachedToWindow();
		mHandler = new Handler();

		/**
		 * requests a tick on the next hard-second boundary
		 */
		mTicker = new Runnable() {
			public void run() {
				if (mTickerStopped)
					return;
				long mCurrentTime = System.currentTimeMillis();
				if (mCurrentTime >= mDeadTime) {
					if (onCountDownListener != null){
						onCountDownListener.onFinish();
					}
					return;
				}
				long mTimeDistance = mDeadTime - mCurrentTime;
				long between = mTimeDistance / 1000;// 转换成秒
				long day = between / (24 * 3600);
				long hour = between % (24 * 3600) / 3600;
				long minute = between % (24 * 3600) % 3600 / 60;
				long second = between % (24 * 3600) % 3600 % 60;
				String deadTimeStr = "距离结束还有" + day + "天" + hour + "小时"
						+ minute + "分" + second + "秒";
				setText(deadTimeStr);

				// mFormat = "距离结束还有dd天kk小时mm分ss秒";//yyyy-MM-dd hh:mm:ss
				// mCalendar.setTimeInMillis(mTimeDistance);//为什么这样计算的时间不对??? 
				// setText(DateFormat.format(mFormat, mCalendar));
				if (onCountDownListener != null)
					onCountDownListener.onTick();
				invalidate();
				long now = SystemClock.uptimeMillis();
				long next = now + (1000 - now % 1000);
				mHandler.postAtTime(mTicker, next);
			}
		};
		mTicker.run();
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		mTickerStopped = true;
	}

	/**
	 * Pulls 12/24 mode from system settings
	 */
	private boolean get24HourMode() {
		return android.text.format.DateFormat.is24HourFormat(getContext());
	}

	private void setFormat() {
		if (get24HourMode()) {
			mFormat = m24;
		} else {
			mFormat = m12;
		}
	}

	private class FormatChangeObserver extends ContentObserver {
		public FormatChangeObserver() {
			super(new Handler());
		}

		@Override
		public void onChange(boolean selfChange) {
			setFormat();
		}
	}

	/**
	 * set the dead time
	 * 
	 * @param deadtime
	 */
	public void setDeadTime(long deadTime) {
		this.mDeadTime = deadTime;
	}

	public interface OnCountDownListener {
		public void onFinish();

		public void onTick();
	}

	public void setOnCountDownListener(OnCountDownListener onCountDownListener) {
		this.onCountDownListener = onCountDownListener;
	}

}


用法:
mClock = (CountDownDigitalClock) findViewById(R.id.myClock);
        mClock.setDeadTime(getDeadTimeFromServer());
        mClock.setOnCountDownListener(new CountDownDigitalClock.OnCountDownListener() {

			@Override
			public void onFinish() {
				// TODO Auto-generated method stub
				showToast("倒计时结束!!!");
			}

			@Override
			public void onTick() {
				// TODO Auto-generated method stub
				Log.i("tag", "执行了"+(count++)+"次");
			}
		});

private long getDeadTimeFromServer(){
    	Calendar mCalendar = Calendar.getInstance();
    	mCalendar.set(2012, 5-1, 18);//月份从0开始
    	mCalendar.set(Calendar.HOUR_OF_DAY, 13);//下午1点
    	mCalendar.set(Calendar.MINUTE, 0);
    	mCalendar.set(Calendar.SECOND, 0);
    	return mCalendar.getTimeInMillis();
}


http://www.23code.com/circletimerview/



Android 倒计时控件,可以定义多种样式。
http://www.jcodecraeer.com/a/opensource/2015/1016/3586.html



CountdownView mCvCountdownViewTest1 = (CountdownView)findViewById(R.id.cv_countdownViewTest1);
mCvCountdownViewTest1.start(995550000); // 毫秒


<cn.iwgang.calendardemo.countdownview.CountdownView
    android:id="@+id/cv_countdownViewTest4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="20dp"
    app:isHideTimeBackground="true"
    app:isShowHour="true"
    app:isShowMillisecond="true"
    app:timeTextColor="#000000"
    app:timeTextSize="25sp"
    app:suffixTextColor="#000000"
    app:suffixTextSize="15sp"
    app:suffixHour=" 时 "
    app:suffixMinute=" 分 "
    app:suffixSecond=" 秒 "
    app:suffixMillisecond=" 毫秒" />

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.os.CountDownTimer;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;

/**
 * 倒计时View
 * Created by iWgang on 15/9/16.
 */
public class CountdownView extends View {
    private Context mContext;
    private int mDay, mHour, mMinute, mSecond, mMillisecond;
    private OnCountdownEndListener mOnCountdownEndListener;
    private CountDownTimer mCountDownTimer;

    private boolean isShowDay;
    private boolean isShowHour;
    private boolean isShowMinute;
    private boolean isShowMillisecond;
    private boolean isHideTimeBackground;
    private boolean isShowTimeBgDivisionLine;

    private Paint mTimeTextPaint;
    private Paint mSuffixTextPaint;
    private Paint mTimeTextBgPaint;
    private Paint mTimeTextBgDivisionLinePaint;

    private RectF mDayBgRectF;
    private RectF mHourBgRectF;
    private RectF mMinuteBgRectF;
    private RectF mSecondBgRectF;
    private RectF mMillisecondBgRectF;

    private float mTimeTextWidth;
    private float mTimeTextHeight;
    private float mTimeTextSize;
    private float mTimeBgSize;
    private int mTimeTextColor;
    private int mTimeBgColor;
    private float mTimeBgRadius;
    private int mTimeBgDivisionLineColor;
    private float mTimeTextBaseY;
    private float mSuffixTextBaseY;
    private int mTimeBgDivisionLineSize;

    // 后缀
    private boolean isHideLastSuffix;
    private String mSuffix;
    private String mSuffixDay;
    private String mSuffixHour;
    private String mSuffixMinute;
    private String mSuffixSecond;
    private String mSuffixMillisecond;
    private int mSuffixTextColor;
    private float mSuffixTextSize;
    private float mSuffixDayTextWidth;
    private float mSuffixHourTextWidth;
    private float mSuffixMinuteTextWidth;
    private float mSuffixSecondTextWidth;
    private float mSuffixMillisecondTextWidth;
    private int mSuffixGravity;

    public CountdownView(Context context) {
        this(context, null);
    }

    public CountdownView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CountdownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.mContext = context;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CountdownView);
        mTimeBgColor = ta.getColor(R.styleable.CountdownView_timeBgColor, 0xFF444444);
        mTimeBgRadius = ta.getDimension(R.styleable.CountdownView_timeBgRadius, 0);
        isShowTimeBgDivisionLine = ta.getBoolean(R.styleable.CountdownView_isShowTimeBgDivisionLine, true);
        mTimeBgDivisionLineColor = ta.getColor(R.styleable.CountdownView_timeBgDivisionLineColor, Color.parseColor("#30FFFFFF"));

        mTimeTextSize = ta.getDimension(R.styleable.CountdownView_timeTextSize, sp2px(12));
        mTimeTextColor = ta.getColor(R.styleable.CountdownView_timeTextColor, 0xFFFFFFFF);
        isHideTimeBackground = ta.getBoolean(R.styleable.CountdownView_isHideTimeBackground, false);
        isShowDay = ta.getBoolean(R.styleable.CountdownView_isShowDay, false);
        isShowHour = ta.getBoolean(R.styleable.CountdownView_isShowHour, true);
        isShowMinute = ta.getBoolean(R.styleable.CountdownView_isShowMinute, true);
        isShowMillisecond = ta.getBoolean(R.styleable.CountdownView_isShowMillisecond, false);

        mSuffixTextSize = ta.getDimension(R.styleable.CountdownView_suffixTextSize, sp2px(12));
        mSuffixTextColor = ta.getColor(R.styleable.CountdownView_suffixTextColor, 0xFF000000);
        mSuffix = ta.getString(R.styleable.CountdownView_suffix);
        mSuffixDay = ta.getString(R.styleable.CountdownView_suffixDay);
        mSuffixHour = ta.getString(R.styleable.CountdownView_suffixHour);
        mSuffixMinute = ta.getString(R.styleable.CountdownView_suffixMinute);
        mSuffixSecond = ta.getString(R.styleable.CountdownView_suffixSecond);
        mSuffixMillisecond = ta.getString(R.styleable.CountdownView_suffixMillisecond);
        mSuffixGravity = ta.getInt(R.styleable.CountdownView_suffixGravity, 1);
        ta.recycle();

        // 初始化画笔
        initPaint();

        // 初始化后缀
        initSuffix();

        // 测量时间文字高度
        Rect rect = new Rect();
        mTimeTextPaint.getTextBounds("00", 0, 2, rect);
        mTimeTextWidth = rect.width();
        mTimeTextHeight = rect.height();

        mTimeBgSize = mTimeTextWidth + (dp2px(2) * 4);

        // 初始化时间背景的RectF对象
        initTimeBgRect();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 初始化时间文字画笔
        mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextPaint.setColor(mTimeTextColor);
        mTimeTextPaint.setTextAlign(Paint.Align.CENTER);
        mTimeTextPaint.setTextSize(mTimeTextSize);

        // 初始化分割文字画笔
        mSuffixTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSuffixTextPaint.setColor(mSuffixTextColor);
        mSuffixTextPaint.setTextSize(mSuffixTextSize);

        // 初始化时间背景画笔
        mTimeTextBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextBgPaint.setStyle(Paint.Style.FILL);
        mTimeTextBgPaint.setColor(mTimeBgColor);

        // 初始化时间背景中间的分割线画笔
        mTimeTextBgDivisionLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextBgDivisionLinePaint.setColor(mTimeBgDivisionLineColor);
        mTimeBgDivisionLineSize = dp2px(0.5f);
        mTimeTextBgDivisionLinePaint.setStrokeWidth(mTimeBgDivisionLineSize);
    }

    private void initSuffix() {
        boolean isSuffixNull = true;
        float mSuffixTextWidth = 0;

        if (!TextUtils.isEmpty(mSuffix)) {
            isSuffixNull = false;
            isHideLastSuffix = true;
            mSuffixTextWidth = mSuffixTextPaint.measureText(mSuffix);
        }

        if (isShowDay) {
            if (!TextUtils.isEmpty(mSuffixDay)) {
                mSuffixDayTextWidth = mSuffixTextPaint.measureText(mSuffixDay);
            } else {
                if (!isSuffixNull) {
                    mSuffixDay = mSuffix;
                    mSuffixDayTextWidth = mSuffixTextWidth;
                }
            }
        }

        if (isShowHour) {
            if (!TextUtils.isEmpty(mSuffixHour)) {
                mSuffixHourTextWidth = mSuffixTextPaint.measureText(mSuffixHour);
            } else {
                if (!isSuffixNull) {
                    mSuffixHour = mSuffix;
                    mSuffixHourTextWidth = mSuffixTextWidth;
                }
            }
        }

        if (isShowMinute) {
            if (!TextUtils.isEmpty(mSuffixMinute)) {
                mSuffixMinuteTextWidth = mSuffixTextPaint.measureText(mSuffixMinute);
            } else if (!isSuffixNull) {
                mSuffixMinute = mSuffix;
                mSuffixMinuteTextWidth = mSuffixTextWidth;
            }
        }

        if (!TextUtils.isEmpty(mSuffixSecond)) {
            mSuffixSecondTextWidth = mSuffixTextPaint.measureText(mSuffixSecond);
        } else if (isShowMillisecond && !isSuffixNull) {
            mSuffixSecond = mSuffix;
            mSuffixSecondTextWidth = mSuffixTextWidth;
        }

        if (isShowMillisecond && isSuffixNull && !TextUtils.isEmpty(mSuffixMillisecond)) {
            mSuffixMillisecondTextWidth = mSuffixTextPaint.measureText(mSuffixMillisecond);
        }
    }

    /**
     * 初始化时间背景的RectF对象
     */
    private void initTimeBgRect() {
        if (!isHideTimeBackground) {
            float mHourLeft = 0;
            float mMinuteLeft;
            float mSecondLeft;

            if (isShowDay) {
                // 显示天
                // 初始化小时背景RectF
                mDayBgRectF = new RectF(0, 0, mTimeBgSize, mTimeBgSize);
                // 计算分钟x轴
                mHourLeft = mTimeBgSize + mSuffixHourTextWidth;
            }

            if (isShowHour) {
                // 显示小时
                // 初始化小时背景RectF
                mHourBgRectF = new RectF(mHourLeft, 0, mTimeBgSize + mHourLeft, mTimeBgSize);
                // 计算分钟x轴
                mMinuteLeft = mHourLeft + mTimeBgSize + mSuffixHourTextWidth;
            } else {
                mMinuteLeft = mHourLeft;
            }

            if (isShowMinute) {
                // 显示分钟
                // 初始化分钟背景RectF
                mMinuteBgRectF = new RectF(mMinuteLeft, 0, mTimeBgSize + mMinuteLeft, mTimeBgSize);
                // 计算秒钟x轴
                mSecondLeft = mMinuteLeft + mTimeBgSize + mSuffixMinuteTextWidth;
            } else {
                mSecondLeft = mMinuteLeft;
            }

            // 初始化秒钟背景RectF
            mSecondBgRectF = new RectF(mSecondLeft, 0, mTimeBgSize + mSecondLeft, mTimeBgSize);

            if (isShowMillisecond) {
                // 计算毫秒x轴
                float mMillisecondLeft = mTimeBgSize + mSuffixSecondTextWidth + mSecondLeft;

                // 初始化毫秒背景RectF
                mMillisecondBgRectF = new RectF(mMillisecondLeft, 0, mTimeBgSize + mMillisecondLeft, mTimeBgSize);
            }

            Paint.FontMetrics timeFontMetrics = mTimeTextPaint.getFontMetrics();
            mTimeTextBaseY = mMinuteBgRectF.top + (mMinuteBgRectF.bottom - mMinuteBgRectF.top - timeFontMetrics.bottom + timeFontMetrics.top)/2 - timeFontMetrics.top;

            Paint.FontMetrics suffixFontMetrics = mSuffixTextPaint.getFontMetrics();
            mSuffixTextBaseY = mMinuteBgRectF.top + (mMinuteBgRectF.bottom - mMinuteBgRectF.top - suffixFontMetrics.bottom + suffixFontMetrics.top)/2 - suffixFontMetrics.top;
        } else {
            Paint.FontMetrics suffixFontMetrics = mSuffixTextPaint.getFontMetrics();
            float suffixFontHeight = suffixFontMetrics.bottom - suffixFontMetrics.top;

            switch (mSuffixGravity) {
                case 0:
                    // top
                    mSuffixTextBaseY = suffixFontHeight - 12; // TODO 待优化
                    break;
                case 1:
                    // center
                    mSuffixTextBaseY = mTimeTextHeight - (mTimeTextHeight - suffixFontHeight)/2 - suffixFontMetrics.bottom;
                    break;
                case 2:
                    // bottom
                    mSuffixTextBaseY = mTimeTextHeight - (mTimeTextHeight - suffixFontHeight);
                    break;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        float timeWidth = isHideTimeBackground ? mTimeTextWidth : mTimeBgSize;
        float width = timeWidth;
        width += (mSuffixDayTextWidth + mSuffixHourTextWidth + mSuffixMinuteTextWidth + mSuffixSecondTextWidth + mSuffixMillisecondTextWidth);

        if (isShowDay) {
            width += timeWidth;
        }

        if (isShowHour) {
            width += timeWidth;
        }

        if (isShowMinute) {
            width += timeWidth;
        }

        if (isShowMillisecond) {
            width += timeWidth;
        }

        setMeasuredDimension((int) width, (isHideTimeBackground ? (int) mTimeTextHeight : (int) mTimeBgSize));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float mHourLeft = 0;
        float mMinuteLeft;
        float mSecondLeft;

        // 背景分割线Y坐标
        float mTimeBgDivisionLineYPos = mTimeBgSize/2 + mTimeBgDivisionLineSize/2;

        if (isHideTimeBackground) {
            // 无背景
            float mTextYPos = mTimeTextHeight;

            // 判断显示天
            if (isShowDay) {
                // 画天文字
                canvas.drawText(formatNum(mDay), mTimeTextWidth/2, mTextYPos, mTimeTextPaint);
                if (mSuffixDayTextWidth > 0) {
                    // 画天后缀
                    canvas.drawText(mSuffixDay, mTimeTextWidth, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算小时x轴
                mHourLeft = mTimeTextWidth + mSuffixDayTextWidth;
            }

            // 判断显示小时
            if (isShowHour) {
                // 画小时文字
                canvas.drawText(formatNum(mHour), mHourLeft + mTimeTextWidth/2, mTextYPos, mTimeTextPaint);
                if (mSuffixHourTextWidth > 0) {
                    // 画小时后缀
                    canvas.drawText(mSuffixHour, mHourLeft + mTimeTextWidth, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算分钟x轴
                mMinuteLeft = mHourLeft + mTimeTextWidth + mSuffixHourTextWidth;
            } else {
                mMinuteLeft = mHourLeft;
            }

            // 判断显示分钟
            if (isShowMinute) {
                // 画分钟文字
                canvas.drawText(formatNum(mMinute), mMinuteLeft + mTimeTextWidth/2 , mTextYPos, mTimeTextPaint);
                if (mSuffixMinuteTextWidth > 0) {
                    // 画分钟后缀
                    canvas.drawText(mSuffixMinute, mMinuteLeft + mTimeTextWidth, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算秒钟x轴
                mSecondLeft = mMinuteLeft + mTimeTextWidth + mSuffixMinuteTextWidth;
            } else {
                mSecondLeft = mMinuteLeft;
            }

            // 画秒钟文字
            canvas.drawText(formatNum(mSecond), mSecondLeft + mTimeTextWidth/2, mTextYPos, mTimeTextPaint);
            if (mSuffixSecondTextWidth > 0) {
                // 画秒钟后缀
                canvas.drawText(mSuffixSecond, mSecondLeft + mTimeTextWidth, mSuffixTextBaseY, mSuffixTextPaint);
            }

            if (isShowMillisecond) {
                // 计算毫秒x轴
                float mMillisecondLeft = mSecondLeft + mTimeTextWidth + mSuffixSecondTextWidth;
                // 画毫秒文字
                canvas.drawText(formatMillisecond(), mMillisecondLeft + mTimeTextWidth / 2, mTextYPos, mTimeTextPaint);
                if (mSuffixMillisecondTextWidth > 0) {
                    // 画毫秒后缀
                    canvas.drawText(mSuffixMillisecond, mMillisecondLeft + mTimeTextWidth, mSuffixTextBaseY, mSuffixTextPaint);
                }
            }
        } else {
            // 有背景

            // 判断显示天
            if (isShowDay) {
                // 画天背景
                canvas.drawRoundRect(mDayBgRectF, mTimeBgRadius, mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // 画天背景中间的横线
                    canvas.drawLine(0, mTimeBgDivisionLineYPos, mTimeBgSize, mTimeBgDivisionLineYPos, mTimeTextBgDivisionLinePaint);
                }
                // 画天文字
                canvas.drawText(formatNum(mHour), mDayBgRectF.centerX(), mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixDayTextWidth > 0) {
                    // 画天后缀
                    canvas.drawText(mSuffixDay, mTimeBgSize, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算小时x轴
                mHourLeft = mTimeBgSize + mSuffixDayTextWidth;
            }

            // 判断显示小时
            if (isShowHour) {
                // 画小时背景
                canvas.drawRoundRect(mHourBgRectF, mTimeBgRadius, mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // 画小时背景中间的横线
                    canvas.drawLine(mHourLeft, mTimeBgDivisionLineYPos, mTimeBgSize + mHourLeft, mTimeBgDivisionLineYPos, mTimeTextBgDivisionLinePaint);
                }
                // 画小时文字
                canvas.drawText(formatNum(mHour), mHourBgRectF.centerX(), mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixHourTextWidth > 0) {
                    // 画小时后缀
                    canvas.drawText(mSuffixHour, mHourLeft + mTimeBgSize, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算分钟x轴
                mMinuteLeft = mHourLeft + mTimeBgSize + mSuffixHourTextWidth;
            } else {
                mMinuteLeft = mHourLeft;
            }

            // 判断显示分钟
            if (isShowMinute) {
                // 画分钟背景
                canvas.drawRoundRect(mMinuteBgRectF, mTimeBgRadius, mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // 画分钟背景中间的横线
                    canvas.drawLine(mMinuteLeft, mTimeBgDivisionLineYPos, mTimeBgSize + mMinuteLeft, mTimeBgDivisionLineYPos, mTimeTextBgDivisionLinePaint);
                }
                // 画分钟文字
                canvas.drawText(formatNum(mMinute), mMinuteBgRectF.centerX(), mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixMinuteTextWidth > 0) {
                    // 画分钟后缀
                    canvas.drawText(mSuffixMinute, mMinuteLeft + mTimeBgSize, mSuffixTextBaseY, mSuffixTextPaint);
                }

                // 计算秒钟x轴
                mSecondLeft = mMinuteLeft + mTimeBgSize + mSuffixMinuteTextWidth;
            } else {
                mSecondLeft = mMinuteLeft;
            }

            // 画秒钟背景
            canvas.drawRoundRect(mSecondBgRectF, mTimeBgRadius, mTimeBgRadius, mTimeTextBgPaint);
            if (isShowTimeBgDivisionLine) {
                // 画秒钟背景中间的横线
                canvas.drawLine(mSecondLeft, mTimeBgDivisionLineYPos, mTimeBgSize + mSecondLeft, mTimeBgDivisionLineYPos, mTimeTextBgDivisionLinePaint);
            }
            // 画秒钟文字
            canvas.drawText(formatNum(mSecond), mSecondBgRectF.centerX(), mTimeTextBaseY, mTimeTextPaint);
            if (mSuffixSecondTextWidth > 0) {
                // 画秒钟后缀
                canvas.drawText(mSuffixSecond, mSecondLeft + mTimeBgSize, mSuffixTextBaseY, mSuffixTextPaint);
            }

            if (isShowMillisecond) {
                // 计算毫秒x轴
                float mMillisecondLeft = mTimeBgSize + mSuffixSecondTextWidth + mSecondLeft;
                // 画毫秒背景
                canvas.drawRoundRect(mMillisecondBgRectF, mTimeBgRadius, mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // 画毫秒背景中间的横线
                    canvas.drawLine(mMillisecondLeft, mTimeBgDivisionLineYPos, mTimeBgSize + mMillisecondLeft, mTimeBgDivisionLineYPos, mTimeTextBgDivisionLinePaint);
                }
                // 画毫秒文字
                canvas.drawText(formatMillisecond(), mMillisecondBgRectF.centerX(), mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixMillisecondTextWidth > 0) {
                    // 画毫秒后缀
                    canvas.drawText(mSuffixMillisecond, mMillisecondLeft + mTimeBgSize, mSuffixTextBaseY, mSuffixTextPaint);
                }
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
        stop();
    }

    /**
     * 启动倒计时
     * @param millisecond 毫秒数
     */
    public void start(long millisecond) {
        if (millisecond <= 0) {
            return ;
        }

        updateShow(millisecond);

        if (null != mCountDownTimer) {
            mCountDownTimer.cancel();
            mCountDownTimer = null;
        }

        mCountDownTimer = new CountDownTimer(millisecond, 10) {
            @Override
            public void onFinish() {
                // 倒计时结束
                // 回调
                if (null != mOnCountdownEndListener) {
                    mOnCountdownEndListener.onEnd();
                }
            }

            @Override
            public void onTick(long millisUntilFinished) {
                updateShow(millisUntilFinished);
            }
        };
        mCountDownTimer.start();
    }

    public void stop() {
        if (null != mCountDownTimer) mCountDownTimer.cancel();
    }

    public void setShowHourView(boolean isShowHour) {
        this.isShowHour = isShowHour;
        invalidate();
    }

    public void setShowMillisecondView(boolean isShowMillisecond) {
        this.isShowMillisecond = isShowMillisecond;
        invalidate();
    }

    public void allShowZero() {
        mHour = 0;
        mMinute = 0;
        mSecond = 0;
        mMillisecond = 0;

        invalidate();
    }

    public void setOnCountdownEndListener(OnCountdownEndListener onCountdownEndListener) {
        this.mOnCountdownEndListener = onCountdownEndListener;
    }

    private void updateShow(long ms) {
        mDay = (int)(ms / (1000 * 60 * 60 * 24));
        mHour = (int)((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        mMinute = (int)((ms % (1000 * 60 * 60)) / (1000 * 60));
        mSecond = (int)((ms % (1000 * 60)) / 1000);
        mMillisecond = (int)(ms % 1000);

        invalidate();
    }

    private String formatNum(int time) {
        return time < 10 ? "0"+time : String.valueOf(time);
    }

    private String formatMillisecond() {
        String retMillisecondStr;

        if (mMillisecond > 99) {
            retMillisecondStr = String.valueOf(mMillisecond).substring(0, 2);
        } else if (mMillisecond <= 9) {
            retMillisecondStr = "0" + mMillisecond;
        } else {
            retMillisecondStr = String.valueOf(mMillisecond);
        }

        return retMillisecondStr;
    }

    public interface OnCountdownEndListener {
        void onEnd();
    }

    public int dp2px(float dpValue) {
        final float scale = mContext.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public float sp2px(float spValue) {
        final float scale = mContext.getResources().getDisplayMetrics().scaledDensity;
        return spValue * scale;
    }

}


<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="CountdownView">
        <attr name="timeBgColor" format="color" />
        <attr name="timeBgRadius" format="dimension" />
        <attr name="isShowTimeBgDivisionLine" format="boolean" />
        <attr name="timeBgDivisionLineColor" format="color" />
        <attr name="timeTextSize" format="dimension" />
        <attr name="suffixTextSize" format="dimension" />
        <attr name="timeTextColor" format="color" />
        <attr name="suffixTextColor" format="color" />
        <attr name="isHideTimeBackground" format="boolean" />
        <attr name="isShowDay" format="boolean" />
        <attr name="isShowHour" format="boolean" />
        <attr name="isShowMinute" format="boolean" />
        <attr name="isShowMillisecond" format="boolean" />
        <attr name="suffix" format="string" />
        <attr name="suffixDay" format="string" />
        <attr name="suffixHour" format="string" />
        <attr name="suffixMinute" format="string" />
        <attr name="suffixSecond" format="string" />
        <attr name="suffixMillisecond" format="string" />
        <attr name="suffixGravity" >
            <enum name="top" value="0" />
            <enum name="center" value="1" />
            <enum name="bottom" value="2" />
        </attr>
    </declare-styleable>

</resources>
  • 大小: 97.4 KB
  • 大小: 19.9 KB
分享到:
评论

相关推荐

    Android自定义TextView倒计时控件

    本文将深入探讨如何基于TextView实现一个倒计时功能,使指定的天数能够动态转化为“N天N小时N分钟N秒”的格式。这个自定义控件非常适合用在诸如活动倒计时、预约截止时间显示等场景。 首先,我们需要创建一个新的...

    TextView自定义计时器(包括倒计时)

    然而,有时候我们需要在TextView中实现更复杂的功能,比如计时器和倒计时器。这通常涉及到自定义View的开发,以扩展TextView的功能。以下是对这个主题的详细解释: 1. **自定义View基础**: 自定义View是Android...

    Android 短信验证倒计时控件

    1. 创建自定义View类:继承TextView或Button,添加倒计时逻辑。 2. 初始化:在构造函数中,获取并解析属性值,设置初始计时时间。 3. 开始倒计时:调用start方法启动倒计时,创建Handler并发送Runnable消息。 4. ...

    html-textview:TextView显示简单HTML

    compile 'org.sufficientlysecure:html-textview:4.0' } 例 &lt; org.sufficientlysecure.htmltextview . HtmlTextView android : id = " @+id/html_text " android : layout_width = " match_parent

    仅使用TextView高仿京东淘宝各种APP活动倒计时样式

    本教程将详细介绍如何仅使用TextView这一基本UI组件来高仿京东、淘宝等热门APP的活动倒计时效果。 首先,我们要了解倒计时的基本原理。在Android中,倒计时通常通过`java.util.Timer`或`CountDownTimer`类实现。`...

    一个封装好的倒计时控件

    1. **自定义View**:倒计时控件通常基于`TextView`进行扩展,创建一个新的类,如`TimingTextView`,继承自`AppCompatTextView`,以便利用TextView的文本显示功能,同时添加倒计时逻辑。 2. **倒计时逻辑**:倒计时...

    仅使用TextView高仿京东淘宝各种APP活动倒计时样式Demo

    本示例项目——“仅使用TextView高仿京东淘宝各种APP活动倒计时样式Demo”旨在教你如何仅通过TextView组件,模仿京东、淘宝等热门电商应用中的活动倒计时效果。这个教程将带你深入理解Android UI设计和自定义View的...

    Android 倒计时程序源码

    开发者可能创建了一个继承自`TextView`或`View`的类,以便在倒计时过程中更新显示的内容,如剩余时间。 3. **线程管理**: 倒计时通常在后台线程执行,以避免阻塞主线程。源码可能使用了`Handler`、`Runnable`或者...

    android的3秒倒计时关闭界面或点击关闭

    在Android应用开发中,有时我们需要实现一种功能,即在用户界面上显示3秒倒计时,用户可以选择在倒计时结束前点击界面来取消自动关闭,或者等待倒计时结束后,程序自动关闭界面。这样的设计可以增加用户体验,给用户...

    Android倒计时 Android仿京东倒计时 Android商城倒计时

    为了实现仿京东的倒计时效果,我们需要自定义一个`View`或者`TextView`,并在此基础上扩展倒计时功能。关键在于如何将剩余时间转换为用户友好的格式,如“XX天XX小时XX分XX秒”。这需要对时间单位进行转换,将总毫秒...

    App启动页倒计时功能

    App启动页倒计时功能是移动应用开发中常见的一种设计,它主要用于提升用户体验,通过显示一个带有倒计时的启动页,用户可以感知到应用正在准备就绪,从而减少等待的焦虑感。在这个示例中,采用了RxJava、RxLifecycle...

    HtmlTextView,在android 3.0或更高版本的textview中显示html代码(特别是处理在互联网上显示图像/图片)。.zip

    `HtmlTextView`是Android开发中的一个开源项目,主要用于在3.0及以上版本的TextView中解析并展示HTML代码,尤其在处理网络上的图像或图片时非常有用。然而,根据描述,这个项目在Android 7.0及更高版本上的表现可能...

    android倒计时控件2

    首先,我们需要创建一个自定义的CountDown类,继承自View或TextView,以封装倒计时逻辑。这个类应该包含以下几个核心属性: 1. **总时长(totalTime)**:倒计时的总秒数。 2. **当前剩余时间(remainingTime)**:...

    andorid 实现验证码 button中textview的倒计时的效果

    在Android开发中,实现验证码Button中的TextView倒计时效果是一个常见的需求,这通常涉及到界面交互和定时任务的处理。下面将详细介绍如何实现这一功能。 首先,我们创建一个自定义的Button,将TextVIew和Button的...

    Android3D翻页效果的倒计时控件

    本知识点将详细介绍如何在Android中实现一个具有3D翻页效果的倒计时控件。这个控件不仅能够显示倒计时,还能通过上下翻转增加视觉吸引力,提升用户体验。 首先,我们需要理解倒计时控件的基本原理。在Android中,倒...

    android倒计时程序

    当倒计时结束,TextView会显示“倒计时结束!”。 对于样式变化,可以有以下几种方式: 1. **数字滚动效果**:你可以使用动画库实现数字逐位滚动的效果,使得倒计时更加动态。这通常通过自定义View或使用现有动画...

    android使用MaterialDialog和AlertDialog弹框Timer倒计时的用法

    本教程将详细讲解如何在Android应用中使用`MaterialDialog`和`AlertDialog`实现倒计时功能,帮助开发者们提升用户体验。 首先,我们先了解`MaterialDialog`。`MaterialDialog`是Android的一款第三方库,它提供了...

    listView复用倒计时控件

    这个类可能扩展了TextView或者自定义了一个ViewGroup,内部包含了一个TextView来显示倒计时。它可能提供了开始、暂停、重置倒计时的方法,并通过回调或者接口通知外部Activity或Fragment倒计时状态的变化。 通过...

    Android 验证码倒计时AS.rar

    2. **TextView的动态更新**:验证码倒计时的界面展示通常是一个TextView,显示剩余的时间,如“60秒后重新发送”。倒计时可以通过每隔一秒更新TextView的文本内容来实现。 3. **CountDownTimer**:Android SDK提供...

Global site tag (gtag.js) - Google Analytics