TextView实现高仿京东活动倒计时

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011557841/article/details/60960860

京东
  电商活动是比较多的,所以活动倒计时功能是必不可少的,自己当初实现的时候是采用复合布局,最近突然发现京东的竟然是用原生的ImageView实现的,666啊!于是就开始Google,终于找到了类似的实现方法,就是利用android的ImageSpan实现的,看到这篇文章后,顿时醒悟,之前有了解过各种Span,没有想到还可以这么用.
  那就开始我们今天的重点
  ImageSpan,

public class BackgroundSpan extends ImageSpan {
    private Rect mTextBound;
    private int maxHeight = 0;
    private int maxWidth = 0;
    private int mPaddingLeft = 20;
    private int mPaddingRight = 20;
    private int mPaddingTop = 20;
    private int mPaddingBottom = 20;
    private int mTextColor = Color.GREEN;
    private int mTextSize = 50;

    public BackgroundSpan(Drawable d, int verticalAlignment) {
        super(d, verticalAlignment);
        mTextBound = new Rect();
    }

    public BackgroundSpan setTimerTextColor(int mTextColor) {
        this.mTextColor = mTextColor;
        return this;
    }

    public BackgroundSpan setTimerTextSize(int textSize) {
        this.mTextSize = textSize;
        return this;
    }

    public BackgroundSpan setTimerPadding(int left, int top, int right, int bottom) {
        this.mPaddingLeft = left;
        this.mPaddingRight = right;
        this.mPaddingBottom = bottom;
        this.mPaddingTop = top;
        return this;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        //繪制文本的內容的背景
        paint.setTextSize(mTextSize);
        //測量文本的寬度和高度,通過mTextBound得到
        paint.getTextBounds(text.toString(), start, end, mTextBound);
        //設定文本背景的寬度和高度,傳入的是left,top,right,bottom四個參數
        maxWidth = maxWidth < mTextBound.width() ? mTextBound.width() : maxWidth;
        maxHeight = maxHeight < mTextBound.height() ? mTextBound.height() : maxHeight;
        //設定最大寬度和最大高度是為了防止在倒計時在數字切換的過程中會重繪,會導致倒計時邊框的寬度和高度會抖動,
        // 所以每次取得最大的高度和寬度而不是每次都去取測量的高度和寬度
        getDrawable().setBounds(0, 0, maxWidth + mPaddingLeft + mPaddingRight, mPaddingTop + mPaddingBottom + maxHeight);
        //設定文本的顏色
        paint.setColor(mTextColor);
        //設定字體的大小
        paint.setTextSize(mTextSize);
        int mGapX = (getDrawable().getBounds().width() - maxWidth) / 2;
        //繪制文本內容
        canvas.drawText(text.subSequence(start, end).toString(), x + mGapX, y , paint);
        //解决设置ImageSpan后其它文字不居中的问题
        Drawable b = getDrawable();
        canvas.save();
        int transY = 0;
        //获得将要显示的文本高度-图片高度除2等居中位置+top(换行情况)
        transY = ((bottom-top) - b.getBounds().bottom)/2+top;
        //偏移画布后开始绘制
        canvas.translate(x, transY);
        b.draw(canvas);
        canvas.restore();
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        Drawable d = getDrawable();
        Rect rect = d.getBounds();
        if (fm != null) {
            //解决设置ImageSpan后其它文字不居中的问题
            Paint.FontMetricsInt fmPaint=paint.getFontMetricsInt();
            //获得文字、图片高度
            int fontHeight = fmPaint.bottom - fmPaint.top;
            int drHeight=rect.bottom-rect.top;

            int top= drHeight/2 - fontHeight/4;
            int bottom=drHeight/2 + fontHeight/4;

            fm.ascent=-bottom;
            fm.top=-bottom;
            fm.bottom=top;
            fm.descent=top;
        }
        return rect.right;
    }
}

  还要简单的说明一下,如果我们只简单的重写draw方法,我们会发现没有设置ImageSpan的文字不会居中,所以我们需要处理一下,核心就是getSize()方法以及draw()里面画布的偏移.
  接下来就是我们今天的第二位明星
  CountDownTimer

public class MyCountDownTimer extends CountDownTimer {
    private Context mContext;
    protected TextView mDateTv;
    private String mTimePattern = "HH:mm:ss";
    private String mTimeStr;
    private int mDrawableId;
    private boolean flag = false;
    protected String[] numbers;//此数组用于生存每个倒计时字符拆分后的天,时,分,秒的数值
    protected List mBackSpanList;
    protected List mTextColorSpanList;
    //用于倒計時樣式的內間距,字體大小,字體顏色,倒計時間隔的顏色
    private int mSpanPaddingLeft, mSpanPaddingRight, mSpanPaddingTop, mSpanPaddingBottom;
    private int mSpanTextSize;
    private int mSpanTextColor;
    protected int mGapSpanColor;
    private SpannableString mSpan;


    public MyCountDownTimer(Context context, long millisInFuture, long countDownInterval, String timePattern, int drawableId, TextView textView) {
        super(millisInFuture, countDownInterval);
        mContext = context;
        mTimePattern = timePattern;
        mDrawableId = drawableId;
        mBackSpanList = new ArrayList<>();
        mTextColorSpanList = new ArrayList<>();
        mDateTv = textView;
    }

    @Override
    public void onTick(long millisUntilFinished) {
        //此处也是一个核心所在,DurationFormatUtils.formatDuration这是apache的org.apache.commons的lang里面的,所以还需要引入它的jar包
        mTimeStr = DurationFormatUtils.formatDuration(millisUntilFinished, mTimePattern);
        setBackgroundSpan(mTimeStr);
    }

    @Override
    public void onFinish() {

    }

    public void setBackgroundSpan(String timeStr) {
        if (!flag) {
            initSpanData(timeStr);
            flag = true;
        }
        int mGapLen = 1;
        mSpan = new SpannableString(timeStr);
        for (int i = 0; i < mBackSpanList.size(); i++) {
            int start = i * numbers[i].length() + i * mGapLen;
            int end = start + numbers[i].length();
            //为数字设置ImageSpan
            setContentSpan(mSpan, mBackSpanList.get(i), start, end);
            if (i < mTextColorSpanList.size()) {//这里为了就是防止12:36:27这种样式,这种样式距离只有2个以是须要做断定,防止数组越界
                setContentSpan(mSpan, mTextColorSpanList.get(i), end, end + mGapLen);
            }
        }
        mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//此要领很主要须要挪用,不然绘制出来的倒计时就是重叠的样式
        mDateTv.setText(mSpan);
    }

    public void initSpanData(String timeStr) {
        numbers = getNumInTimerStr(timeStr);
        for (int i = 0; i < numbers.length; i++) {
            BackgroundSpan backgroundColorSpan = new BackgroundSpan(ContextCompat.getDrawable(mContext, mDrawableId), ALIGN_BASELINE);
            initBackSpanStyle(backgroundColorSpan);
            mBackSpanList.add(backgroundColorSpan);
        }
    }

    protected void initBackSpanStyle(BackgroundSpan mBackSpan) {
        mBackSpan.setTimerPadding(mSpanPaddingLeft, mSpanPaddingTop, mSpanPaddingRight, mSpanPaddingBottom);
        mBackSpan.setTimerTextColor(mSpanTextColor);
        mBackSpan.setTimerTextSize(mSpanTextSize);
    }


    public void cancelTimer() {
        this.cancel();
    }

    public void startTimer() {
        this.start();
    }

    public String getmTimeStr() {
        return mTimeStr;
    }

    public MyCountDownTimer setTimerTextSize(int textSize) {
        this.mSpanTextSize = textSize;
        return this;
    }

    public MyCountDownTimer setTimerPadding(int left, int top, int right, int bottom) {
        this.mSpanPaddingLeft = left;
        this.mSpanPaddingBottom = bottom;
        this.mSpanPaddingRight = right;
        this.mSpanPaddingTop = top;
        return this;
    }

    public MyCountDownTimer setTimerTextColor(int color) {
        this.mSpanTextColor = color;
        return this;
    }

    public MyCountDownTimer setTimerGapColor(int color) {
        this.mGapSpanColor = color;
        return this;
    }
    //得到倒计时数字部分
    public static String[] getNumInTimerStr(String mTimerStr){
        return mTimerStr.split("[^\\d]");
    }
    //设置内容的Span
    public static void setContentSpan(SpannableString mSpan, Object span, int start,
                                      int end) {
        mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

  使用方法如下:

new MyCountDownTimer(mContext,1000*60*60,1000,"HH:mm:ss",
R.drawable.timer_shape,mTextViewDurationFormat)
.setTimerPadding(10,10,10,10)
                .setTimerTextColor(Color.YELLOW)
                .setTimerTextSize(16)
                .setTimerGapColor(Color.BLACK);

参考文章:
http://blog.csdn.net/gaoyucindy/article/details/39473135

猜你喜欢

转载自blog.csdn.net/u011557841/article/details/60960860