倒计时view

1.自定义view

package cc.feeing.www.loldianjing.selfview;

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

import cc.feeing.www.loldianjing.R;


/**
 * Countdown View Created by iWgang on 15/9/16.
 * https://github.com/iwgang/CountdownView
 */
public class CountdownView extends View {
    private static final String DEFAULT_SUFFIX = ":";
    private static final float DEFAULT_SUFFIX_LR_MARGIN = 3; // dp
    private static final float DEFAULT_TIME_BG_DIVISION_LINE_SIZE = 0.5f; // dp

    private Context mContext;
    private int mDay, mHour, mMinute, mSecond, mMillisecond;
    private long mRemainTime;
    private OnCountdownEndListener mOnCountdownEndListener;
    private End end;
    private OnCountdownIntervalListener mOnCountdownIntervalListener;
    private CustomCountDownTimer mCustomCountDownTimer;
    private boolean isShowDay, isShowHour, isShowMinute, isShowSecond,
            isShowMillisecond;
    private boolean mHasSetIsShowDay, mHasSetIsShowHour;
    private boolean isHideTimeBackground;
    private boolean isShowTimeBgDivisionLine;
    private boolean isTimeTextBold;
    private boolean isSuffixTextBold;
    private Paint mTimeTextPaint;
    private Paint mSuffixTextPaint;
    private Paint mTimeTextBgPaint;
    private Paint mTimeTextBgDivisionLinePaint;

    private RectF mDayBgRectF, mHourBgRectF, mMinuteBgRectF, mSecondBgRectF,
            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 mTimeBgDivisionLineSize;
    private float mTimeTextBaseY;
    private float mTimeBgDivisionLineYPos;

    private String mSuffix, mSuffixDay, mSuffixHour, mSuffixMinute,
            mSuffixSecond, mSuffixMillisecond;
    private int mSuffixTextColor;
    private float mSuffixTextSize;
    private float mSuffixDayTextWidth, mSuffixHourTextWidth,
            mSuffixMinuteTextWidth, mSuffixSecondTextWidth,
            mSuffixMillisecondTextWidth;
    private int mSuffixGravity;
    private float mSuffixLRMargin;
    private float mSuffixDayLeftMargin, mSuffixDayRightMargin;
    private float mSuffixHourLeftMargin, mSuffixHourRightMargin;
    private float mSuffixMinuteLeftMargin, mSuffixMinuteRightMargin;
    private float mSuffixSecondLeftMargin, mSuffixSecondRightMargin;
    private float mSuffixMillisecondLeftMargin;
    private float mSuffixDayTextBaseline, mSuffixHourTextBaseline,
            mSuffixMinuteTextBaseline, mSuffixSecondTextBaseline,
            mSuffixMillisecondTextBaseline;
    private float mTempSuffixDayLeftMargin, mTempSuffixDayRightMargin;
    private float mTempSuffixHourLeftMargin, mTempSuffixHourRightMargin;
    private float mTempSuffixMinuteLeftMargin, mTempSuffixMinuteRightMargin;
    private float mTempSuffixSecondLeftMargin, mTempSuffixSecondRightMargin;
    private float mTempSuffixMillisecondLeftMargin;
    private String mTempSuffixMinute, mTempSuffixSecond;
    private float mTimeTextBaseline;
    private float mLeftPaddingSize;
    private float mTopPaddingSize;
    private int mContentAllWidth;
    private int mContentAllHeight;
    private int mViewWidth;
    private int mViewHeight;
    private int mTimeTextBottom;

    private float mDayTimeTextWidth;
    private float mDayTimeBgWidth;
    private boolean isDayLargeNinetyNine;

    private long mInterval;
    private long mPreviouIntervalCallbackTime;
    private boolean hasCustomSomeSuffix = false;

    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"));
        mTimeBgDivisionLineSize = ta.getDimension(
                R.styleable.CountdownView_timeBgDivisionLineSize,
                dp2px(DEFAULT_TIME_BG_DIVISION_LINE_SIZE));
        mTimeBgSize = ta.getDimension(R.styleable.CountdownView_timeBgSize, 0);
        isTimeTextBold = ta.getBoolean(
                R.styleable.CountdownView_isTimeTextBold, false);
        mTimeTextSize = ta.getDimension(R.styleable.CountdownView_timeTextSize,
                sp2px(12));
        mTimeTextColor = ta.getColor(R.styleable.CountdownView_timeTextColor,
                0xFF000000);
        isHideTimeBackground = ta.getBoolean(
                R.styleable.CountdownView_isHideTimeBackground, true);
        isShowDay = ta.getBoolean(R.styleable.CountdownView_isShowDay, false);
        isShowHour = ta.getBoolean(R.styleable.CountdownView_isShowHour, false);
        isShowMinute = ta.getBoolean(R.styleable.CountdownView_isShowMinute,
                true);
        isShowSecond = ta.getBoolean(R.styleable.CountdownView_isShowSecond,
                true);
        isShowMillisecond = ta.getBoolean(
                R.styleable.CountdownView_isShowMillisecond, false);

        mHasSetIsShowDay = ta.hasValue(R.styleable.CountdownView_isShowDay);
        mHasSetIsShowHour = ta.hasValue(R.styleable.CountdownView_isShowHour);

        isSuffixTextBold = ta.getBoolean(
                R.styleable.CountdownView_isSuffixTextBold, 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);
        mSuffixLRMargin = ta.getDimension(
                R.styleable.CountdownView_suffixLRMargin, -1);
        mSuffixDayLeftMargin = ta.getDimension(
                R.styleable.CountdownView_suffixDayLeftMargin, -1);
        mSuffixDayRightMargin = ta.getDimension(
                R.styleable.CountdownView_suffixDayRightMargin, -1);
        mSuffixHourLeftMargin = ta.getDimension(
                R.styleable.CountdownView_suffixHourLeftMargin, -1);
        mSuffixHourRightMargin = ta.getDimension(
                R.styleable.CountdownView_suffixHourRightMargin, -1);
        mSuffixMinuteLeftMargin = ta.getDimension(
                R.styleable.CountdownView_suffixMinuteLeftMargin, -1);
        mSuffixMinuteRightMargin = ta.getDimension(
                R.styleable.CountdownView_suffixMinuteRightMargin, -1);
        mSuffixSecondLeftMargin = ta.getDimension(
                R.styleable.CountdownView_suffixSecondLeftMargin, -1);
        mSuffixSecondRightMargin = ta.getDimension(
                R.styleable.CountdownView_suffixSecondRightMargin, -1);
        mSuffixMillisecondLeftMargin = ta.getDimension(
                R.styleable.CountdownView_suffixMillisecondLeftMargin, -1);
        ta.recycle();

        // temporarily saved suffix left and right margins
        mTempSuffixDayLeftMargin = mSuffixDayLeftMargin;
        mTempSuffixDayRightMargin = mSuffixDayRightMargin;
        mTempSuffixHourLeftMargin = mSuffixHourLeftMargin;
        mTempSuffixHourRightMargin = mSuffixHourRightMargin;
        mTempSuffixMinuteLeftMargin = mSuffixMinuteLeftMargin;
        mTempSuffixMinuteRightMargin = mSuffixMinuteRightMargin;
        mTempSuffixSecondLeftMargin = mSuffixSecondLeftMargin;
        mTempSuffixSecondRightMargin = mSuffixSecondRightMargin;
        mTempSuffixMillisecondLeftMargin = mSuffixMillisecondLeftMargin;

        mTempSuffixMinute = mSuffixMinute;
        mTempSuffixSecond = mSuffixSecond;

        // initialize
        initPaint();
        initSuffix(true);
        initSuffixMargin();

        // regular time data
        // pick one of two (minute and second)
        if (!isShowMinute && !isShowSecond) {
            isShowSecond = true;
        }
        if (!isShowSecond) {
            isShowMillisecond = false;
        }

        // initialize time text width and height
        Rect rect = new Rect();
        mTimeTextPaint.getTextBounds("00", 0, 2, rect);
        mTimeTextWidth = rect.width();
        mTimeTextHeight = rect.height();
        mTimeTextBottom = rect.bottom;

        if (!isHideTimeBackground && mTimeBgSize < mTimeTextWidth) {
            mTimeBgSize = mTimeTextWidth + (dp2px(2) * 4);
        }
    }

    /**
     * initialize Paint
     */
    private void initPaint() {
        // time text
        mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextPaint.setColor(mTimeTextColor);
        mTimeTextPaint.setTextAlign(Paint.Align.CENTER);
        mTimeTextPaint.setTextSize(mTimeTextSize);
        if (isTimeTextBold) {
            mTimeTextPaint.setFakeBoldText(true);
        }

        // suffix text
        mSuffixTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSuffixTextPaint.setColor(mSuffixTextColor);
        mSuffixTextPaint.setTextSize(mSuffixTextSize);
        if (isSuffixTextBold) {
            mSuffixTextPaint.setFakeBoldText(true);
        }

        // time background
        mTimeTextBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextBgPaint.setStyle(Paint.Style.FILL);
        mTimeTextBgPaint.setColor(mTimeBgColor);

        // time background division line
        mTimeTextBgDivisionLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextBgDivisionLinePaint.setColor(mTimeBgDivisionLineColor);
        mTimeTextBgDivisionLinePaint.setStrokeWidth(mTimeBgDivisionLineSize);
    }

    /**
     * initialize suffix
     */
    private void initSuffix(boolean isInit) {
        boolean isSuffixNull = true;
        float mSuffixTextWidth = 0;
        float mDefSuffixTextWidth = mSuffixTextPaint
                .measureText(DEFAULT_SUFFIX);

        if (!TextUtils.isEmpty(mSuffix)) {
            isSuffixNull = false;
            mSuffixTextWidth = mSuffixTextPaint.measureText(mSuffix);
        }
        boolean isSetSuffixDay = !TextUtils.isEmpty(mSuffixDay);
        boolean isSetSuffixHour = !TextUtils.isEmpty(mSuffixHour);
        boolean isSetSuffixMinute = !TextUtils.isEmpty(mSuffixMinute);
        boolean isSetSuffixSecond = !TextUtils.isEmpty(mSuffixSecond);
        boolean isSetSuffixMillisecond = !TextUtils.isEmpty(mSuffixMillisecond);

        if (isInit) {
            if ((isShowDay && isSetSuffixDay)
                    || (isShowHour && isSetSuffixHour)
                    || (isShowMinute && isSetSuffixMinute)
                    || (isShowSecond && isSetSuffixSecond)
                    || (isShowMillisecond && isSetSuffixMillisecond)) {
                hasCustomSomeSuffix = true;
            }
        }

        if (isShowDay) {
            if (isSetSuffixDay) {
                mSuffixDayTextWidth = mSuffixTextPaint.measureText(mSuffixDay);
            } else {
                if (!isSuffixNull) {
                    mSuffixDay = mSuffix;
                    mSuffixDayTextWidth = mSuffixTextWidth;
                } else if (!hasCustomSomeSuffix) {
                    mSuffixDay = DEFAULT_SUFFIX;
                    mSuffixDayTextWidth = mDefSuffixTextWidth;
                }
            }
        } else {
            mSuffixDayTextWidth = 0;
        }

        if (isShowHour) {
            if (isSetSuffixHour) {
                mSuffixHourTextWidth = mSuffixTextPaint
                        .measureText(mSuffixHour);
            } else {
                if (!isSuffixNull) {
                    mSuffixHour = mSuffix;
                    mSuffixHourTextWidth = mSuffixTextWidth;
                } else if (!hasCustomSomeSuffix) {
                    mSuffixHour = DEFAULT_SUFFIX;
                    mSuffixHourTextWidth = mDefSuffixTextWidth;
                }
            }
        } else {
            mSuffixHourTextWidth = 0;
        }

        if (isShowMinute) {
            if (isSetSuffixMinute) {
                mSuffixMinuteTextWidth = mSuffixTextPaint
                        .measureText(mSuffixMinute);
            } else if (isShowSecond) {
                if (!isSuffixNull) {
                    mSuffixMinute = mSuffix;
                    mSuffixMinuteTextWidth = mSuffixTextWidth;
                } else if (!hasCustomSomeSuffix) {
                    mSuffixMinute = DEFAULT_SUFFIX;
                    mSuffixMinuteTextWidth = mDefSuffixTextWidth;
                }
            } else {
                mSuffixMinuteTextWidth = 0;
            }
        } else {
            mSuffixMinuteTextWidth = 0;
        }

        if (isShowSecond) {
            if (isSetSuffixSecond) {
                mSuffixSecondTextWidth = mSuffixTextPaint
                        .measureText(mSuffixSecond);
            } else if (isShowMillisecond) {
                if (!isSuffixNull) {
                    mSuffixSecond = mSuffix;
                    mSuffixSecondTextWidth = mSuffixTextWidth;
                } else if (!hasCustomSomeSuffix) {
                    mSuffixSecond = DEFAULT_SUFFIX;
                    mSuffixSecondTextWidth = mDefSuffixTextWidth;
                }
            } else {
                mSuffixSecondTextWidth = 0;
            }
        } else {
            mSuffixSecondTextWidth = 0;
        }

        if (isShowMillisecond && hasCustomSomeSuffix && isSetSuffixMillisecond) {
            mSuffixMillisecondTextWidth = mSuffixTextPaint
                    .measureText(mSuffixMillisecond);
        } else {
            mSuffixMillisecondTextWidth = 0;
        }
    }

    /**
     * initialize suffix margin
     */
    private void initSuffixMargin() {
        int defSuffixLRMargin = dp2px(DEFAULT_SUFFIX_LR_MARGIN);
        boolean isSuffixLRMarginNull = true;

        if (mSuffixLRMargin >= 0) {
            isSuffixLRMarginNull = false;
        }

        if (isShowDay && mSuffixDayTextWidth > 0) {
            if (mSuffixDayLeftMargin < 0) {
                if (!isSuffixLRMarginNull) {
                    mSuffixDayLeftMargin = mSuffixLRMargin;
                } else {
                    mSuffixDayLeftMargin = defSuffixLRMargin;
                }
            }

            if (mSuffixDayRightMargin < 0) {
                if (!isSuffixLRMarginNull) {
                    mSuffixDayRightMargin = mSuffixLRMargin;
                } else {
                    mSuffixDayRightMargin = defSuffixLRMargin;
                }
            }
        } else {
            mSuffixDayLeftMargin = 0;
            mSuffixDayRightMargin = 0;
        }

        if (isShowHour && mSuffixHourTextWidth > 0) {
            if (mSuffixHourLeftMargin < 0) {
                if (!isSuffixLRMarginNull) {
                    mSuffixHourLeftMargin = mSuffixLRMargin;
                } else {
                    mSuffixHourLeftMargin = defSuffixLRMargin;
                }
            }

            if (mSuffixHourRightMargin < 0) {
                if (!isSuffixLRMarginNull) {
                    mSuffixHourRightMargin = mSuffixLRMargin;
                } else {
                    mSuffixHourRightMargin = defSuffixLRMargin;
                }
            }
        } else {
            mSuffixHourLeftMargin = 0;
            mSuffixHourRightMargin = 0;
        }

        if (isShowMinute && mSuffixMinuteTextWidth > 0) {
            if (mSuffixMinuteLeftMargin < 0) {
                if (!isSuffixLRMarginNull) {
                    mSuffixMinuteLeftMargin = mSuffixLRMargin;
                } else {
                    mSuffixMinuteLeftMargin = defSuffixLRMargin;
                }
            }

            if (isShowSecond) {
                if (mSuffixMinuteRightMargin < 0) {
                    if (!isSuffixLRMarginNull) {
                        mSuffixMinuteRightMargin = mSuffixLRMargin;
                    } else {
                        mSuffixMinuteRightMargin = defSuffixLRMargin;
                    }
                }
            } else {
                mSuffixMinuteRightMargin = 0;
            }
        } else {
            mSuffixMinuteLeftMargin = 0;
            mSuffixMinuteRightMargin = 0;
        }

        if (isShowSecond) {
            if (mSuffixSecondTextWidth > 0) {
                if (mSuffixSecondLeftMargin < 0) {
                    if (!isSuffixLRMarginNull) {
                        mSuffixSecondLeftMargin = mSuffixLRMargin;
                    } else {
                        mSuffixSecondLeftMargin = defSuffixLRMargin;
                    }
                }

                if (isShowMillisecond) {
                    if (mSuffixSecondRightMargin < 0) {
                        if (!isSuffixLRMarginNull) {
                            mSuffixSecondRightMargin = mSuffixLRMargin;
                        } else {
                            mSuffixSecondRightMargin = defSuffixLRMargin;
                        }
                    }
                } else {
                    mSuffixSecondRightMargin = 0;
                }
            } else {
                mSuffixSecondLeftMargin = 0;
                mSuffixSecondRightMargin = 0;
            }

            if (isShowMillisecond && mSuffixMillisecondTextWidth > 0) {
                if (mSuffixMillisecondLeftMargin < 0) {
                    if (!isSuffixLRMarginNull) {
                        mSuffixMillisecondLeftMargin = mSuffixLRMargin;
                    } else {
                        mSuffixMillisecondLeftMargin = defSuffixLRMargin;
                    }
                }
            } else {
                mSuffixMillisecondLeftMargin = 0;
            }
        } else {
            mSuffixSecondLeftMargin = 0;
            mSuffixSecondRightMargin = 0;
            mSuffixMillisecondLeftMargin = 0;
        }
    }

    /**
     * initialize time initialize rectF
     */
    private void initTimeBgRect() {
        if (!isHideTimeBackground) {
            float mHourLeft;
            float mMinuteLeft;
            float mSecondLeft;

            if (isShowDay) {
                // initialize day background rectF
                mDayBgRectF = new RectF(mLeftPaddingSize, mTopPaddingSize,
                        mLeftPaddingSize + mDayTimeBgWidth, mTopPaddingSize
                        + mDayTimeBgWidth);
                // hour left point
                mHourLeft = mLeftPaddingSize + mDayTimeBgWidth
                        + mSuffixDayTextWidth + mSuffixDayLeftMargin
                        + mSuffixDayRightMargin;
            } else {
                // hour left point
                mHourLeft = mLeftPaddingSize;
            }

            if (isShowHour) {
                // initialize hour background rectF
                mHourBgRectF = new RectF(mHourLeft, mTopPaddingSize, mHourLeft
                        + mTimeBgSize, mTopPaddingSize + mTimeBgSize);
                // minute left point
                mMinuteLeft = mHourLeft + mTimeBgSize + mSuffixHourTextWidth
                        + mSuffixHourLeftMargin + mSuffixHourRightMargin;
            } else {
                // minute left point
                mMinuteLeft = mHourLeft;
            }

            if (isShowMinute) {
                // initialize minute background rectF
                mMinuteBgRectF = new RectF(mMinuteLeft, mTopPaddingSize,
                        mMinuteLeft + mTimeBgSize, mTopPaddingSize
                        + mTimeBgSize);
                // second left point
                mSecondLeft = mMinuteLeft + mTimeBgSize
                        + mSuffixMinuteTextWidth + mSuffixMinuteLeftMargin
                        + mSuffixMinuteRightMargin;
            } else {
                // second left point
                mSecondLeft = mMinuteLeft;
            }

            if (isShowSecond) {
                // initialize second background rectF
                mSecondBgRectF = new RectF(mSecondLeft, mTopPaddingSize,
                        mSecondLeft + mTimeBgSize, mTopPaddingSize
                        + mTimeBgSize);

                if (isShowMillisecond) {
                    // millisecond left point
                    float mMillisecondLeft = mSecondLeft + mTimeBgSize
                            + mSuffixSecondTextWidth + mSuffixSecondLeftMargin
                            + mSuffixSecondRightMargin;

                    // initialize millisecond background rectF
                    mMillisecondBgRectF = new RectF(mMillisecondLeft,
                            mTopPaddingSize, mMillisecondLeft + mTimeBgSize,
                            mTopPaddingSize + mTimeBgSize);
                }
            }

            // time text baseline
            Paint.FontMetrics timeFontMetrics = mTimeTextPaint.getFontMetrics();
            mTimeTextBaseY = mSecondBgRectF.top
                    + (mSecondBgRectF.bottom - mSecondBgRectF.top
                    - timeFontMetrics.bottom + timeFontMetrics.top) / 2
                    - timeFontMetrics.top - mTimeTextBottom;

            // initialize background division line y point
            mTimeBgDivisionLineYPos = mSecondBgRectF.centerY()
                    + (mTimeBgDivisionLineSize == dp2px(DEFAULT_TIME_BG_DIVISION_LINE_SIZE) ? mTimeBgDivisionLineSize
                    : mTimeBgDivisionLineSize / 2);
        }
    }

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

        mContentAllWidth = getAllContentWidth();
        mContentAllHeight = isHideTimeBackground ? (int) mTimeTextHeight
                : (int) mTimeBgSize;

        mViewWidth = measureSize(1, mContentAllWidth, widthMeasureSpec);
        mViewHeight = measureSize(2, mContentAllHeight, heightMeasureSpec);

        setMeasuredDimension(mViewWidth, mViewHeight);

        initTimeTextBaselineAndTimeBgTopPadding();
        initLeftPaddingSize();
        initTimeBgRect();
    }

    /**
     * initialize time text baseline and time background top padding
     */
    private void initTimeTextBaselineAndTimeBgTopPadding() {
        if (getPaddingTop() == getPaddingBottom()) {
            // center
            mTimeTextBaseline = mViewHeight / 2 + mTimeTextHeight / 2
                    - mTimeTextBottom;

            mTopPaddingSize = (mViewHeight - mContentAllHeight) / 2;
        } else {
            // padding top
            mTimeTextBaseline = mViewHeight - (mViewHeight - getPaddingTop())
                    + mTimeTextHeight - mTimeTextBottom;

            mTopPaddingSize = getPaddingTop();
        }

        if (isShowDay && mSuffixDayTextWidth > 0) {
            mSuffixDayTextBaseline = getSuffixTextBaseLine(mSuffixDay);
        }

        if (isShowHour && mSuffixHourTextWidth > 0) {
            mSuffixHourTextBaseline = getSuffixTextBaseLine(mSuffixHour);
        }

        if (isShowMinute && mSuffixMinuteTextWidth > 0) {
            mSuffixMinuteTextBaseline = getSuffixTextBaseLine(mSuffixMinute);
        }

        if (mSuffixSecondTextWidth > 0) {
            mSuffixSecondTextBaseline = getSuffixTextBaseLine(mSuffixSecond);
        }

        if (isShowMillisecond && mSuffixMillisecondTextWidth > 0) {
            mSuffixMillisecondTextBaseline = getSuffixTextBaseLine(mSuffixMillisecond);
        }
    }

    private float getSuffixTextBaseLine(String suffixText) {
        Rect tempRect = new Rect();
        mSuffixTextPaint.getTextBounds(suffixText, 0, suffixText.length(),
                tempRect);

        float ret;
        switch (mSuffixGravity) {
            case 0:
                // top
                if (isHideTimeBackground) {
                    ret = mTimeTextBaseline - mTimeTextHeight - tempRect.top;
                } else {
                    ret = mTopPaddingSize - tempRect.top;
                }
                break;
            default:
            case 1:
                // center
                if (isHideTimeBackground) {
                    ret = mTimeTextBaseline - mTimeTextHeight / 2
                            + tempRect.height() / 2;
                } else {
                    ret = mTopPaddingSize + mTimeBgSize - mTimeBgSize / 2
                            + tempRect.height() / 2;
                }
                break;
            case 2:
                // bottom
                if (isHideTimeBackground) {
                    ret = mTimeTextBaseline - tempRect.bottom;
                } else {
                    ret = mTopPaddingSize + mTimeBgSize - tempRect.bottom;
                }
                break;
        }

        return ret;
    }

    /**
     * initialize view left padding
     */
    private void initLeftPaddingSize() {
        if (getPaddingLeft() == getPaddingRight()) {
            // center
            mLeftPaddingSize = (mViewWidth - mContentAllWidth) / 2;
        } else {
            // padding left
            mLeftPaddingSize = getPaddingLeft();
        }
    }

    /**
     * measure view Size
     *
     * @param specType    1 width 2 height
     * @param contentSize all content view size
     * @param measureSpec spec
     * @return measureSize
     */
    private int measureSize(int specType, int contentSize, int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = Math.max(contentSize, specSize);
        } else {
            result = contentSize;

            if (specType == 1) {
                // width
                result += (getPaddingLeft() + getPaddingRight());
            } else {
                // height
                result += (getPaddingTop() + getPaddingBottom());
            }
        }

        return result;
    }

    /**
     * get all view width
     *
     * @return all view width
     */
    private int getAllContentWidth() {
        float timeWidth = isHideTimeBackground ? mTimeTextWidth : mTimeBgSize;
        float width = (mSuffixDayTextWidth + mSuffixHourTextWidth
                + mSuffixMinuteTextWidth + mSuffixSecondTextWidth + mSuffixMillisecondTextWidth);
        width += (mSuffixDayLeftMargin + mSuffixDayRightMargin
                + mSuffixHourLeftMargin + mSuffixHourRightMargin
                + mSuffixMinuteLeftMargin + mSuffixMinuteRightMargin
                + mSuffixSecondLeftMargin + mSuffixSecondRightMargin + mSuffixMillisecondLeftMargin);

        if (isShowDay) {
            if (isDayLargeNinetyNine) {
                Rect rect = new Rect();
                String tempDay = String.valueOf(mDay);
                mTimeTextPaint
                        .getTextBounds(tempDay, 0, tempDay.length(), rect);
                mDayTimeTextWidth = rect.width();

                if (!isHideTimeBackground) {
                    mDayTimeBgWidth = mDayTimeTextWidth + (dp2px(2) * 4);
                    width += mDayTimeBgWidth;
                } else {
                    width += mDayTimeTextWidth;
                }
            } else {
                mDayTimeTextWidth = mTimeTextWidth;
                mDayTimeBgWidth = mTimeBgSize;
                width += timeWidth;
            }
        }

        if (isShowHour) {
            width += timeWidth;
        }

        if (isShowMinute) {
            width += timeWidth;
        }

        if (isShowSecond) {
            width += timeWidth;
        }

        if (isShowMillisecond) {
            width += timeWidth;
        }

        return (int) Math.ceil(width);
    }

    private void refTimeShow(boolean isShowDay, boolean isShowHour,
                             boolean isShowMinute, boolean isShowSecond,
                             boolean isShowMillisecond) {
        boolean isRef = false;

        if (this.isShowDay != isShowDay) {
            this.isShowDay = isShowDay;
            isRef = true;

            // reset day margins
            if (isShowDay) {
                mSuffixDayLeftMargin = mTempSuffixDayLeftMargin;
                mSuffixDayRightMargin = mTempSuffixDayRightMargin;
            }
        }

        if (this.isShowHour != isShowHour) {
            this.isShowHour = isShowHour;
            isRef = true;

            // reset hour margins
            if (isShowHour) {
                mSuffixHourLeftMargin = mTempSuffixHourLeftMargin;
                mSuffixHourRightMargin = mTempSuffixHourRightMargin;
            }
        }

        if (this.isShowMinute != isShowMinute) {
            this.isShowMinute = isShowMinute;
            isRef = true;

            // reset minute margins
            if (isShowMinute) {
                mSuffixMinuteLeftMargin = mTempSuffixMinuteLeftMargin;
                mSuffixMinuteRightMargin = mTempSuffixMinuteRightMargin;
                mSuffixMinute = mTempSuffixMinute;
            }
        }

        boolean isModCountdownInterval = false;

        if (this.isShowSecond != isShowSecond) {
            this.isShowSecond = isShowSecond;
            isRef = true;
            isModCountdownInterval = true;

            // reset second margins
            if (isShowSecond) {
                mSuffixSecondLeftMargin = mTempSuffixSecondLeftMargin;
                mSuffixSecondRightMargin = mTempSuffixSecondRightMargin;
                mSuffixSecond = mTempSuffixSecond;
            } else {
                mSuffixMinute = mTempSuffixMinute;
            }

            mSuffixMinuteLeftMargin = mTempSuffixMinuteLeftMargin;
            mSuffixMinuteRightMargin = mTempSuffixMinuteRightMargin;
        }

        if (this.isShowMillisecond != isShowMillisecond) {
            this.isShowMillisecond = isShowMillisecond;
            isRef = true;
            isModCountdownInterval = true;

            // reset millisecond margins
            if (isShowMillisecond) {
                mSuffixMillisecondLeftMargin = mTempSuffixMillisecondLeftMargin;
            } else {
                mSuffixSecond = mTempSuffixSecond;
            }

            mSuffixSecondLeftMargin = mTempSuffixSecondLeftMargin;
            mSuffixSecondRightMargin = mTempSuffixSecondRightMargin;
        }

        // judge modify countdown interval
        if (isModCountdownInterval) {
            start(mRemainTime);
        }

        if (isRef) {
            initSuffix(false);
            initSuffixMargin();
            requestLayout();
        }
    }

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

        float mHourLeft;
        float mMinuteLeft;
        float mSecondLeft;

        if (isHideTimeBackground) {
            // no background

            if (isShowDay) {
                // draw day text
                canvas.drawText(formatNum(mDay), mLeftPaddingSize
                                + mDayTimeTextWidth / 2, mTimeTextBaseline,
                        mTimeTextPaint);
                if (mSuffixDayTextWidth > 0) {
                    // draw day suffix
                    canvas.drawText(mSuffixDay, mLeftPaddingSize
                                    + mDayTimeTextWidth + mSuffixDayLeftMargin,
                            mSuffixDayTextBaseline, mSuffixTextPaint);
                }

                // hour left point
                mHourLeft = mLeftPaddingSize + mDayTimeTextWidth
                        + mSuffixDayTextWidth + mSuffixDayLeftMargin
                        + mSuffixDayRightMargin;
            } else {
                // hour left point
                mHourLeft = mLeftPaddingSize;
            }

            if (isShowHour) {
                // draw hour text
                canvas.drawText(formatNum(mHour), mHourLeft + mTimeTextWidth
                        / 2, mTimeTextBaseline, mTimeTextPaint);
                if (mSuffixHourTextWidth > 0) {
                    // draw hour suffix
                    canvas.drawText(mSuffixHour, mHourLeft + mTimeTextWidth
                                    + mSuffixHourLeftMargin, mSuffixHourTextBaseline,
                            mSuffixTextPaint);
                }

                // minute left point
                mMinuteLeft = mHourLeft + mTimeTextWidth + mSuffixHourTextWidth
                        + mSuffixHourLeftMargin + mSuffixHourRightMargin;
            } else {
                // minute left point
                mMinuteLeft = mHourLeft;
            }

            if (isShowMinute) {
                // draw minute text
                canvas.drawText(formatNum(mMinute), mMinuteLeft
                        + mTimeTextWidth / 2, mTimeTextBaseline, mTimeTextPaint);
                if (mSuffixMinuteTextWidth > 0) {
                    // draw minute suffix
                    canvas.drawText(mSuffixMinute, mMinuteLeft + mTimeTextWidth
                                    + mSuffixMinuteLeftMargin,
                            mSuffixMinuteTextBaseline, mSuffixTextPaint);
                }

                // second left point
                mSecondLeft = mMinuteLeft + mTimeTextWidth
                        + mSuffixMinuteTextWidth + mSuffixMinuteLeftMargin
                        + mSuffixMinuteRightMargin;
            } else {
                // second left point
                mSecondLeft = mMinuteLeft;
            }

            if (isShowSecond) {
                // draw second text
                canvas.drawText(formatNum(mSecond), mSecondLeft
                        + mTimeTextWidth / 2, mTimeTextBaseline, mTimeTextPaint);
                if (mSuffixSecondTextWidth > 0) {
                    // draw second suffix
                    canvas.drawText(mSuffixSecond, mSecondLeft + mTimeTextWidth
                                    + mSuffixSecondLeftMargin,
                            mSuffixSecondTextBaseline, mSuffixTextPaint);
                }

                if (isShowMillisecond) {
                    // millisecond left point
                    float mMillisecondLeft = mSecondLeft + mTimeTextWidth
                            + mSuffixSecondTextWidth + mSuffixSecondLeftMargin
                            + mSuffixSecondRightMargin;
                    // draw millisecond text
                    canvas.drawText(formatMillisecond(), mMillisecondLeft
                                    + mTimeTextWidth / 2, mTimeTextBaseline,
                            mTimeTextPaint);
                    if (mSuffixMillisecondTextWidth > 0) {
                        // draw millisecond suffix
                        canvas.drawText(mSuffixMillisecond,
                                mMillisecondLeft + mTimeTextWidth
                                        + mSuffixMillisecondLeftMargin,
                                mSuffixMillisecondTextBaseline,
                                mSuffixTextPaint);
                    }
                }
            }
        } else {
            // show background

            if (isShowDay) {
                // draw day background
                canvas.drawRoundRect(mDayBgRectF, mTimeBgRadius, mTimeBgRadius,
                        mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // draw day background division line
                    canvas.drawLine(mLeftPaddingSize, mTimeBgDivisionLineYPos,
                            mLeftPaddingSize + mDayTimeBgWidth,
                            mTimeBgDivisionLineYPos,
                            mTimeTextBgDivisionLinePaint);
                }
                // draw day text
                canvas.drawText(formatNum(mDay), mDayBgRectF.centerX(),
                        mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixDayTextWidth > 0) {
                    // draw day suffix
                    canvas.drawText(mSuffixDay, mLeftPaddingSize
                                    + mDayTimeBgWidth + mSuffixDayLeftMargin,
                            mSuffixDayTextBaseline, mSuffixTextPaint);
                }

                // hour left point
                mHourLeft = mLeftPaddingSize + mDayTimeBgWidth
                        + mSuffixDayTextWidth + mSuffixDayLeftMargin
                        + mSuffixDayRightMargin;
            } else {
                // hour left point
                mHourLeft = mLeftPaddingSize;
            }

            if (isShowHour) {
                // draw hour background
                canvas.drawRoundRect(mHourBgRectF, mTimeBgRadius,
                        mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // draw hour background division line
                    canvas.drawLine(mHourLeft, mTimeBgDivisionLineYPos,
                            mTimeBgSize + mHourLeft, mTimeBgDivisionLineYPos,
                            mTimeTextBgDivisionLinePaint);
                }
                // draw hour text
                canvas.drawText(formatNum(mHour), mHourBgRectF.centerX(),
                        mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixHourTextWidth > 0) {
                    // draw hour suffix
                    canvas.drawText(mSuffixHour, mHourLeft + mTimeBgSize
                                    + mSuffixHourLeftMargin, mSuffixHourTextBaseline,
                            mSuffixTextPaint);
                }

                // minute left point
                mMinuteLeft = mHourLeft + mTimeBgSize + mSuffixHourTextWidth
                        + mSuffixHourLeftMargin + mSuffixHourRightMargin;
            } else {
                // minute left point
                mMinuteLeft = mHourLeft;
            }

            if (isShowMinute) {
                // draw minute background
                canvas.drawRoundRect(mMinuteBgRectF, mTimeBgRadius,
                        mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // draw minute background division line
                    canvas.drawLine(mMinuteLeft, mTimeBgDivisionLineYPos,
                            mTimeBgSize + mMinuteLeft, mTimeBgDivisionLineYPos,
                            mTimeTextBgDivisionLinePaint);
                }
                // draw minute text
                canvas.drawText(formatNum(mMinute), mMinuteBgRectF.centerX(),
                        mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixMinuteTextWidth > 0) {
                    // draw minute suffix
                    canvas.drawText(mSuffixMinute, mMinuteLeft + mTimeBgSize
                                    + mSuffixMinuteLeftMargin,
                            mSuffixMinuteTextBaseline, mSuffixTextPaint);
                }

                // second left point
                mSecondLeft = mMinuteLeft + mTimeBgSize
                        + mSuffixMinuteTextWidth + mSuffixMinuteLeftMargin
                        + mSuffixMinuteRightMargin;
            } else {
                // second left point
                mSecondLeft = mMinuteLeft;
            }

            if (isShowSecond) {
                // draw second background
                canvas.drawRoundRect(mSecondBgRectF, mTimeBgRadius,
                        mTimeBgRadius, mTimeTextBgPaint);
                if (isShowTimeBgDivisionLine) {
                    // draw second background division line
                    canvas.drawLine(mSecondLeft, mTimeBgDivisionLineYPos,
                            mTimeBgSize + mSecondLeft, mTimeBgDivisionLineYPos,
                            mTimeTextBgDivisionLinePaint);
                }
                // draw second text
                canvas.drawText(formatNum(mSecond), mSecondBgRectF.centerX(),
                        mTimeTextBaseY, mTimeTextPaint);
                if (mSuffixSecondTextWidth > 0) {
                    // draw second suffix
                    canvas.drawText(mSuffixSecond, mSecondLeft + mTimeBgSize
                                    + mSuffixSecondLeftMargin,
                            mSuffixSecondTextBaseline, mSuffixTextPaint);
                }

                if (isShowMillisecond) {
                    // millisecond left point
                    float mMillisecondLeft = mSecondLeft + mTimeBgSize
                            + mSuffixSecondTextWidth + mSuffixSecondLeftMargin
                            + mSuffixSecondRightMargin;
                    // draw millisecond background
                    canvas.drawRoundRect(mMillisecondBgRectF, mTimeBgRadius,
                            mTimeBgRadius, mTimeTextBgPaint);
                    if (isShowTimeBgDivisionLine) {
                        // draw millisecond background division line
                        canvas.drawLine(mMillisecondLeft,
                                mTimeBgDivisionLineYPos, mTimeBgSize
                                        + mMillisecondLeft,
                                mTimeBgDivisionLineYPos,
                                mTimeTextBgDivisionLinePaint);
                    }
                    // draw millisecond text
                    canvas.drawText(formatMillisecond(),
                            mMillisecondBgRectF.centerX(), mTimeTextBaseY,
                            mTimeTextPaint);
                    if (mSuffixMillisecondTextWidth > 0) {
                        // draw millisecond suffix
                        canvas.drawText(mSuffixMillisecond, mMillisecondLeft
                                        + mTimeBgSize + mSuffixMillisecondLeftMargin,
                                mSuffixMillisecondTextBaseline,
                                mSuffixTextPaint);
                    }
                }
            }
        }
    }

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

    }

    /**
     * start countdown
     *
     * @param millisecond millisecond
     */
    public void start(long millisecond) {
        if (millisecond <= 0) {
            return;
        }

        if (null != mCustomCountDownTimer) {
            mCustomCountDownTimer.stop();
            mCustomCountDownTimer = null;
        }

        long countDownInterval;
        if (isShowMillisecond) {
            countDownInterval = 10;
            updateShow(millisecond);
        } else {
            countDownInterval = 1000;
        }

        mCustomCountDownTimer = new CustomCountDownTimer(millisecond,
                countDownInterval) {
            @Override
            public void onTick(long millisUntilFinished) {
                updateShow(millisUntilFinished);
            }

            @Override
            public void onFinish() {
                // countdown end
                allShowZero();
                // callback
                if (null != mOnCountdownEndListener) {
                    mOnCountdownEndListener.onEnd(CountdownView.this);
                }
            }
        };
        mCustomCountDownTimer.start();
    }

    /**
     * stop countdown
     */
    public void stop() {
        if (null != mCustomCountDownTimer)
            mCustomCountDownTimer.stop();
    }

    /**
     * pause countdown
     */
    public void pause() {
        if (null != mCustomCountDownTimer)
            mCustomCountDownTimer.pause();
    }

    /**
     * pause countdown
     */
    public void restart() {
        if (null != mCustomCountDownTimer)
            mCustomCountDownTimer.restart();
    }

    /**
     * custom time show
     *
     * @param isShowDay         isShowDay
     * @param isShowHour        isShowHour
     * @param isShowMinute      isShowMinute
     * @param isShowSecond      isShowSecond
     * @param isShowMillisecond isShowMillisecond
     */
    public void customTimeShow(boolean isShowDay, boolean isShowHour,
                               boolean isShowMinute, boolean isShowSecond,
                               boolean isShowMillisecond) {
        mHasSetIsShowDay = true;
        mHasSetIsShowHour = true;

        // regular time data
        // pick one of two (minute and second)
        if (!isShowMinute && !isShowSecond) {
            isShowSecond = true;
        }
        if (!isShowSecond) {
            isShowMillisecond = false;
        }

        refTimeShow(isShowDay, isShowHour, isShowMinute, isShowSecond,
                isShowMillisecond);
    }

    /**
     * set all time zero
     */
    public void allShowZero() {
        mHour = 0;
        mMinute = 0;
        mSecond = 0;
        mMillisecond = 0;
        end.onEnd();
        invalidate();
    }

    /**
     * set countdown end callback listener
     *
     * @param onCountdownEndListener OnCountdownEndListener
     */
    public void setOnCountdownEndListener(
            OnCountdownEndListener onCountdownEndListener) {
        this.mOnCountdownEndListener = onCountdownEndListener;
    }

    /**
     * set interval callback listener
     *
     * @param interval                    interval time
     * @param onCountdownIntervalListener OnCountdownIntervalListener
     */
    public void setOnCountdownIntervalListener(long interval,
                                               OnCountdownIntervalListener onCountdownIntervalListener) {
        this.mInterval = interval;
        this.mOnCountdownIntervalListener = onCountdownIntervalListener;
    }

    /**
     * get day
     *
     * @return current day
     */
    public int getDay() {
        return mDay;
    }

    /**
     * get hour
     *
     * @return current hour
     */
    public int getHour() {
        return mHour;
    }

    /**
     * get minute
     *
     * @return current minute
     */
    public int getMinute() {
        return mMinute;
    }

    /**
     * get second
     *
     * @return current second
     */
    public int getSecond() {
        return mSecond;
    }

    /**
     * get remain time
     *
     * @return remain time ( millisecond )
     */
    public long getRemainTime() {
        return mRemainTime;
    }

    public void updateShow(long ms) {
        this.mRemainTime = ms;

        mDay = (int) (ms / (1000 * 60 * 60 * 24));
        if (isShowDay) {
            mHour = (int) ((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        } else {
            mHour = (int) ((ms % (1000 * 60 * 60 * 60)) / (1000 * 60 * 60));
        }
        mMinute = (int) ((ms % (1000 * 60 * 60)) / (1000 * 60));
        mSecond = (int) ((ms % (1000 * 60)) / 1000);
        mMillisecond = (int) (ms % 1000);

        // interval callback
        if (mInterval > 0 && null != mOnCountdownIntervalListener) {
            if (mPreviouIntervalCallbackTime == 0) {
                mPreviouIntervalCallbackTime = ms;
            } else if (ms + mInterval <= mPreviouIntervalCallbackTime) {
                mPreviouIntervalCallbackTime = ms;
                mOnCountdownIntervalListener.onInterval(this, mRemainTime);
            }
        }

        handlerAutoShowTimeAndDayLargeNinetyNine();
        invalidate();
    }

    public void setEnd(Context context, End end) {
        this.mContext = context;
        this.end = end;

    }

    private void handlerAutoShowTimeAndDayLargeNinetyNine() {
        if (!mHasSetIsShowDay) {
            if (!isShowDay && mDay > 0) {
                // auto show day
                // judge auto show hour
                if (!mHasSetIsShowHour) {
                    refTimeShow(true, true, isShowMinute, isShowSecond,
                            isShowMillisecond);
                } else {
                    refTimeShow(true, isShowHour, isShowMinute, isShowSecond,
                            isShowMillisecond);
                }
            } else if (isShowDay && mDay == 0) {
                // auto hide day
                refTimeShow(false, isShowHour, isShowMinute, isShowSecond,
                        isShowMillisecond);
            } else {
                if (!mHasSetIsShowHour) {
                    if (!isShowHour && (mDay > 0 || mHour > 0)) {
                        // auto show hour
                        refTimeShow(isShowDay, true, isShowMinute,
                                isShowSecond, isShowMillisecond);
                    } else if (isShowHour && mDay == 0 && mHour == 0) {
                        // auto hide hour
                        refTimeShow(false, false, isShowMinute, isShowSecond,
                                isShowMillisecond);
                    }
                }
            }
        } else {
            if (!mHasSetIsShowHour) {
                if (!isShowHour && (mDay > 0 || mHour > 0)) {
                    // auto show hour
                    refTimeShow(isShowDay, true, isShowMinute, isShowSecond,
                            isShowMillisecond);
                } else if (isShowHour && mDay == 0 && mHour == 0) {
                    // auto hide hour
                    refTimeShow(isShowDay, false, isShowMinute, isShowSecond,
                            isShowMillisecond);
                }
            }
        }

        if (isShowDay) {
            // handler large ninety nine
            if (!isDayLargeNinetyNine && mDay > 99) {
                isDayLargeNinetyNine = true;
                requestLayout();
            } else if (isDayLargeNinetyNine && mDay <= 99) {
                isDayLargeNinetyNine = false;
                requestLayout();
            }
        }
    }

    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 / 10);
        } else if (mMillisecond <= 9) {
            retMillisecondStr = "0" + mMillisecond;
        } else {
            retMillisecondStr = String.valueOf(mMillisecond);
        }

        return retMillisecondStr;
    }

    public interface OnCountdownEndListener {
        void onEnd(CountdownView cv);
    }

    public interface End {
        void onEnd();
    }

    public interface OnCountdownIntervalListener {
        void onInterval(CountdownView cv, long remainTime);
    }

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

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

}
2,工具类

package cc.feeing.www.loldianjing.selfview;




import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;

/**
 * 使用android.os.CountDownTimer的源码
 * 1. 对回调onTick做了细小调整,已解决最后1秒不会倒计时到0,要等待2秒才回调onFinish
 * 2. 添加了一些自定义方法
 * Created by iWgang on 15/10/18.
 * https://github.com/iwgang/CountdownView
 */
public abstract class CustomCountDownTimer {
    private static final int MSG = 1;
    private final long mMillisInFuture;
    private final long mCountdownInterval;
    private long mStopTimeInFuture;
    private long mPauseTimeInFuture;
    private boolean isStop = false;
    private boolean isPause = false;

    /**
     * @param millisInFuture    总倒计时时间
     * @param countDownInterval 倒计时间隔时间
     */
    public CustomCountDownTimer(long millisInFuture, long countDownInterval) {
        // 解决秒数有时会一开始就减去了2秒问题(如10秒总数的,刚开始就8999,然后没有不会显示9秒,直接到8秒)
        if (countDownInterval > 1000) millisInFuture += 15;
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    private synchronized CustomCountDownTimer start(long millisInFuture) {
        isStop = false;
        if (millisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + millisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        return this;
    }

    /**
     * 开始倒计时
     */
    public synchronized final void start() {
        start(mMillisInFuture);
    }

    /**
     * 停止倒计时
     */
    public synchronized final void stop() {
        isStop = true;
        mHandler.removeMessages(MSG);
    }

    /**
     * 暂时倒计时
     * 调用{@link #restart()}方法重新开始
     */
    public synchronized final void pause() {
        if (isStop) return ;

        isPause = true;
        mPauseTimeInFuture = mStopTimeInFuture - SystemClock.elapsedRealtime();
        mHandler.removeMessages(MSG);
    }

    /**
     * 重新开始
     */
    public synchronized final void restart() {
        if (isStop || !isPause) return ;

        isPause = false;
        start(mPauseTimeInFuture);
    }

    /**
     * 倒计时间隔回调
     * @param millisUntilFinished 剩余毫秒数
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * 倒计时结束回调
     */
    public abstract void onFinish();


    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            synchronized (CustomCountDownTimer.this) {
                if (isStop || isPause) {
                    return;
                }

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                if (millisLeft <= 0) {
                    onFinish();
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };
}
3,属性
<declare-styleable name="CountdownView">
        <attr name="isHideTimeBackground" format="boolean" />
        <attr name="isShowTimeBgDivisionLine" format="boolean" />
        <attr name="timeBgDivisionLineColor" format="color" />
        <attr name="timeBgDivisionLineSize" format="dimension" />
        <attr name="timeBgColor" format="color" />
        <attr name="timeBgSize" format="dimension" />
        <attr name="timeBgRadius" format="dimension" />
        <attr name="isTimeTextBold" format="boolean" />
        <attr name="timeTextSize" format="dimension" />
        <attr name="timeTextColor" format="color" />
        <attr name="isShowDay" format="boolean" />
        <attr name="isShowHour" format="boolean" />
        <attr name="isShowMinute" format="boolean" />
        <attr name="isShowSecond" format="boolean" />
        <attr name="isShowMillisecond" format="boolean" />
        <attr name="isSuffixTextBold" format="boolean" />
        <attr name="suffixTextSize" format="dimension" />
        <attr name="suffixTextColor" format="color" />
        <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>
        <attr name="suffixLRMargin" format="dimension" />
        <attr name="suffixDayLeftMargin" format="dimension" />
        <attr name="suffixDayRightMargin" format="dimension" />
        <attr name="suffixHourLeftMargin" format="dimension" />
        <attr name="suffixHourRightMargin" format="dimension" />
        <attr name="suffixMinuteLeftMargin" format="dimension" />
        <attr name="suffixMinuteRightMargin" format="dimension" />
        <attr name="suffixSecondLeftMargin" format="dimension" />
        <attr name="suffixSecondRightMargin" format="dimension" />
        <attr name="suffixMillisecondLeftMargin" format="dimension" />
    </declare-styleable>

4.布局使用


 <cc.feeing.www.loldianjing.selfview.CountdownView
            android:id="@+id/cd_down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:isHideTimeBackground="true"
            app:isShowDay="false"
            app:isShowHour="true"
            app:isShowMillisecond="false"
            app:isShowMinute="true"
            app:isShowSecond="true"
            app:suffixGravity="center"
            app:suffixTextColor="#ffb600"
            app:suffixTextSize="13sp"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/juli"
            android:layout_gravity="center"
            app:timeTextColor="#ffb600"
            app:timeTextSize="13sp"
            />






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值