Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中使用TextView實現高仿京東淘寶各種倒計時效果

Android中使用TextView實現高仿京東淘寶各種倒計時效果

編輯:關於Android編程

今天給大家帶來的是僅僅使用一個TextView實現一個高仿京東、淘寶、唯品會等各種電商APP的活動倒計時。最近公司一直加班也沒來得及時間去整理,今天難得休息想把這個分享給大家,只求共同學習,以及自己後續的復習。為什麼會想到使用一個TextView來實現呢?因為最近公司在做一些優化的工作,其中就有一個倒計時樣式,原來開發的這個控件的同事使用了多個TextView拼接在一起的,實現的代碼冗余比較大,故此項目經理就說:小宏這個就交給你來優化了,並且還要保證有一定的擴展性,當時就懵逼了。不知道從何處開始優化。然後我就查看京東,餓了麼,唯品會等各個APP的倒計時,並在開發者中打開層級界面顯示,發現他們都有一個共同的特點就是一個View,沒有使用多個TextView來拼接。相信大家都知道僅僅使用一個TextView比使用多個TextView拼接去實現的優勢吧,下面不妨來看看幾個界面就知道了。



看到這個,大家心裡自然就想到了自定義View來實現吧。對,自定義View確實可以實現這樣的效果。但是今天我們不采用自定義View來做。而是使用一個TextView來實現。

由於項目經理要求此次優化的代碼具有可擴展性。所以此次代碼的設計加了一些面向對象的知識。有一些自己的設計和架構的思路。

此次demo的設計思路:

          1、編寫一個倒計時的基類作為實現最普通和最基本的倒計時的功能,沒有任何樣式,讓這個基類去繼承CountDownTimer類,並且在該基類中

保存一個TextView的對象,並且把每次倒計時的數據,顯示在TextView中,然後公布一個getmDateTv()方法返回一個TextView對象即可。然後只要拿到這個TextView對象顯示界面的布局中即可。非常方便。

          2、然後不同樣式的倒計時,只需要編寫不同的子類去繼承最普通的倒計時基類即可,然後重寫其中的設置數據和設置樣式的兩個方法即可,然後就能給最普通的倒計時添加不同的樣式。下次如果需要擴展新的倒計時樣式,不需要改變其他類的代碼,只需編寫一個普通倒計時的派生類重寫兩個方法即可,使得可擴展性更靈活。

          3、然後通過一個TimerUtils管理類,去集中承擔子類和父類壓力,讓子類和父類所需實現功能分擔到TimerUtils類中,並且該TimerUtils管理類是與客戶端唯一打交道的類,比如獲得倒計時對象以及獲得倒計時的TextView對象都通過這個管理類分配,避免客戶端直接與倒計時的基類和子類打交道。從而使得類的封裝性和隱藏性得到體現。

下面可以看下這個Demo設計的簡單的UML類圖:


通過以上思路分析下面我們就看看此次Demo的實現需要用到哪些知識點.

 1、CountDownTimer類的用法。

    2、SpannableString的用法。

    3、MikyouCountDownTimer的封裝。

    4、自定義MikyouBackgroundSpan的實現。

一、通過以上的分析我們首先得復習一下有關CountDownTimer的知識,CountDownTimer是一個很簡單的類我們可以看下的它的源碼,它的用法自然就知道了。

CountDownTimer是一個抽象類。

// 
// Source code recreated from a .class file by IntelliJ IDEA 
// (powered by Fernflower decompiler) 
// 
package android.os; 
public abstract class CountDownTimer { 
public CountDownTimer(long millisInFuture, long countDownInterval) { 
throw new RuntimeException("Stub!"); 
} 
public final synchronized void cancel() { 
throw new RuntimeException("Stub!"); 
} 
public final synchronized CountDownTimer start() { 
throw new RuntimeException("Stub!"); 
} 
public abstract void onTick(long var1); 
public abstract void onFinish(); 
}

可以看到倒計時的總時長就是millisFuture,和countDownInterVal間隔步長默認是1000ms,所以數據都是通過其構造器進行初始化,然後需要去重寫一個回調方法onTick,裡面的一個參數就是每隔相應的步長後剩余的時間毫秒數。然後我們只需要在onTick方法中將每隔1000ms時間毫秒數進行時間格式化即可得到相應時間格式的倒計時這就是實現了最基本倒計時樣式。格式化倒計時格式采用的是apache中的common的lang包中DurationFormatUtils類中的formatDuration,通過傳入一個時間格式就會自動將倒計時轉換成相應的mTimePattern的樣式(HH:mm:ss或dd天HH時mm分ss秒).

二、復習一下有關SpannableString的用法。

在Android中EditText用於編輯文本,TextView用於顯示文本,但是有時候我們需要對其中的文本進行樣式等方面的設置。Android為我們提供了SpannableString類來對指定文本進行處理。

1) ForegroundColorSpan 文本顏色

private void setForegroundColorSpan() {
SpannableString spanString = new SpannableString("前景色");
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.append(spanString);
}

2) BackgroundColorSpan 文本背景色

private void setBackgroundColorSpan() {
SpannableString spanString = new SpannableString("背景色");
BackgroundColorSpan span = new BackgroundColorSpan(Color.YELLOW);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.append(spanString);
}

3) StyleSpan 字體樣式:粗體、斜體等

private void setStyleSpan() { 
SpannableString spanString = new SpannableString("粗體斜體"); 
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC); 
spanString.setSpan(span, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

4) RelativeSizeSpan 相對大小

private void setRelativeFontSpan() { 
SpannableString spanString = new SpannableString("字體相對大小"); 
spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

5) TypefaceSpan 文本字體

private void setTypefaceSpan() { 
SpannableString spanString = new SpannableString("文本字體"); 
spanString.setSpan(new TypefaceSpan("monospace"), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanText); 
}

6) URLSpan 文本超鏈接

private void addUrlSpan() { 
SpannableString spanString = new SpannableString("超鏈接"); 
URLSpan span = new URLSpan("http://www.baidu.com"); 
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

7) ImageSpan 圖片

private void addImageSpan() { 
SpannableString spanString = new SpannableString(" "); 
Drawable d = getResources().getDrawable(R.drawable.ic_launcher); 
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); 
spanString.setSpan(span, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

8) ClickableSpan 文本有點擊事件

private TextView textView; 
textView = (TextView)this.findViewById(R.id.textView); 
String text = "顯示Activity"; 
SpannableString spannableString = new SpannableString(text); 
spannableString.setSpan(new ClickableSpan() { 
@Override 
public void onClick(View widget) { 
Intent intent = new Intent(Main.this,OtherActivity.class); 
startActivity(intent); 
} 
// 表示點擊整個text的長度都有效觸發這個事件 
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 
textView.setMovementMethod(LinkMovementMethod.getInstance());

9) UnderlineSpan 下劃線

private void addUnderLineSpan() { 
SpannableString spanString = new SpannableString("下劃線"); 
UnderlineSpan span = new UnderlineSpan(); 
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

10) StrikethroughSpan

刪除線

private void addStrikeSpan() { 
SpannableString spanString = new SpannableString("刪除線"); 
StrikethroughSpan span = new StrikethroughSpan(); 
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

11) SuggestionSpan

相當於占位符

12) MaskFilterSpan

修飾效果,如模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter)

13) RasterizerSpan

光柵效果

14) AbsoluteSizeSpan

絕對大小(文本字體)

private void setAbsoluteFontSpan() { 
SpannableString spannableString = new SpannableString("40號字體"); 
AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(40); 
spannableString.setSpan(absoluteSizeSpan, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
editText.append(spannableString); 
}

15) DynamicDrawableSpan 設置圖片,基於文本基線或底部對齊。

16) TextAppearanceSpan

文本外貌(包括字體、大小、樣式和顏色)

private void setTextAppearanceSpan() { 
SpannableString spanString = new SpannableString("文本外貌"); 
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(this, android.R.style.TextAppearance_Medium); 
spanString.setSpan(textAppearanceSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

好了,通過以上的復習知識點,現在我們就可以來真正開始demo的實現,然後我們一起來一步一步封裝我們的倒計時。

一、編寫一個MikyouCountDownTimer基類,讓它去繼承CountDownTimer類,並且公布出initSpanData和setBackgroundSpan方法用於其他樣式倒計時的子類使用,它可以實現最基本倒計時的功能。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.os.CountDownTimer; 
import android.text.style.ForegroundColorSpan; 
import android.widget.TextView; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
import org.apache.commons.lang.time.DurationFormatUtils; 
import java.util.ArrayList; 
import java.util.List; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class MikyouCountDownTimer extends CountDownTimer{ 
private Context mContext;//傳入的上下文對象 
protected TextView mDateTv;//一個TextView實現倒計時 
private long mGapTime;//傳入設置的時間間隔即倒計時的總時長 
private long mCount = 1000;//倒計時的步長 一般為1000代表每隔1s跳一次 
private String mTimePattern = "HH:mm:ss";//timePattern 傳入的時間的樣式 如: HH:mm:ss HH時mm分ss秒 dd天HH時mm分ss秒 
private String mTimeStr; 
protected List<MikyouBackgroundSpan> mBackSpanList; 
protected List<ForegroundColorSpan> mTextColorSpanList; 
private int mDrawableId; 
private boolean flag = false;//設置標記flag,用於控制使得初始化Span的數據一次 
protected String[] numbers;//此數組用於保存每個倒計時字符拆分後的天,時,分,秒的數值 
protected char[] nonNumbers;//保存了天,時,分,秒之間的間隔("天","時","分","秒"或者":") 
//用於倒計時樣式的內間距,字體大小,字體顏色,倒計時間隔的顏色 
private int mSpanPaddingLeft,mSpanPaddingRight,mSpanPaddingTop,mSpanPaddingBottom; 
private int mSpanTextSize; 
private int mSpanTextColor; 
protected int mGapSpanColor; 
public MikyouCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
this(mContext,mGapTime,1000,mTimePattern,mDrawableId); 
} 
public MikyouCountDownTimer(Context mContext, long mGapTime, int mCount, String mTimePattern,int mDrawableId) { 
super(mGapTime,mCount); 
this.mContext = mContext; 
this.mGapTime = mGapTime;//倒計時總時長 
this.mCount = mCount;//每次倒計時的步長,默認是1000 
this.mDrawableId= mDrawableId;//用於設置背景的drawable的id 
this.mTimePattern = mTimePattern;//時間的格式:如HH:mm:ss或者dd天HH時mm分ss秒等 
mBackSpanList = new ArrayList<>(); 
mTextColorSpanList = new ArrayList<>(); 
mDateTv = new TextView(mContext,null); 
} 
//公布這些設置倒計時樣式的方法,供外部調用,從而靈活定制倒計時的樣式 
public MikyouCountDownTimer setTimerTextSize(int textSize){ 
this.mSpanTextSize = textSize; 
return this; 
} 
public MikyouCountDownTimer setTimerPadding(int left,int top,int right,int bottom){ 
this.mSpanPaddingLeft = left; 
this.mSpanPaddingBottom = bottom; 
this.mSpanPaddingRight = right; 
this.mSpanPaddingTop = top; 
return this; 
} 
public MikyouCountDownTimer setTimerTextColor(int color){ 
this.mSpanTextColor = color; 
return this; 
} 
public MikyouCountDownTimer setTimerGapColor(int color){ 
this.mGapSpanColor = color; 
return this; 
} 
//設置倒計時的Span的樣式,公布出給各個子類實現 
public void setBackgroundSpan(String timeStr) { 
if (!flag){ 
initSpanData(timeStr); 
flag = true; 
} 
mDateTv.setText(timeStr); 
} 
//設置倒計時的Span的數據,公布出給各個子類實現 
public void initSpanData(String timeStr) { 
numbers = TimerUtils.getNumInTimerStr(timeStr); 
nonNumbers = TimerUtils.getNonNumInTimerStr(timeStr); 
} 
protected void initBackSpanStyle(MikyouBackgroundSpan mBackSpan) { 
mBackSpan.setTimerPadding(mSpanPaddingLeft,mSpanPaddingTop,mSpanPaddingRight,mSpanPaddingBottom); 
mBackSpan.setTimerTextColor(mSpanTextColor); 
mBackSpan.setTimerTextSize(mSpanTextSize); 
} 
@Override 
public void onTick(long l) { 
if (l > 0) { 
mTimeStr = DurationFormatUtils.formatDuration(l, mTimePattern); 
//這是apache中的common的lang包中DurationFormatUtils類中的formatDuration,通過傳入 
//一個時間格式就會自動將倒計時轉換成相應的mTimePattern的樣式(HH:mm:ss或dd天HH時mm分ss秒) 
setBackgroundSpan(mTimeStr); 
} 
} 
@Override 
public void onFinish() { 
mDateTv.setText("倒計時結束"); 
} 
//用於返回顯示倒計時的TextView的對象 
public TextView getmDateTv() { 
startTimer(); 
return mDateTv; 
} 
public void cancelTimer(){ 
this.cancel(); 
} 
public void startTimer(){ 
this.start(); 
} 
public String getmTimeStr() { 
return mTimeStr; 
} 
}

TimerUtils類用於保存不同倒計時的格式,例如HH:mm:ss、HH時mm分ss秒、dd天HH時mm分ss秒等。現在我們可以來看下簡單的基本樣式。

二、自定義MikyouBackgroundSpan去繼承ImageSpan,這個類非常重要是用於給倒計時的TextView加樣式,為什麼可以使用一個TextView來實現呢

別忘了還有個很強悍的類就是SpannableString類,這個類就是可以設置一段字符串中的每個字符的樣式,很多樣式。最後通過TextView中有個setSpan方法即可傳入
一個SpannableString對象完成設置。但是為什麼需要自定義一個Span呢?這是因為很奇怪為什麼android中的那麼多Span樣式中沒有一個可以直接設置一個drawable對象文件呢,所以上網找了很多都沒有找到,最後在stackOverFlow上找到了一個外國人給了一個解決辦法,就是重寫ImageSpan最後就可以實現了設置drawable文件即可

package com.mikyou.countdowntimer.myview; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.graphics.drawable.Drawable; 
import android.text.style.ImageSpan; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class MikyouBackgroundSpan 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 MikyouBackgroundSpan(Drawable d, int verticalAlignment) { 
super(d, verticalAlignment); 
mTextBound = new Rect(); 
} 
public MikyouBackgroundSpan setTimerTextColor(int mTextColor) { 
this.mTextColor = mTextColor; 
return this; 
} 
public MikyouBackgroundSpan setTimerTextSize(int textSize){ 
this.mTextSize = textSize; 
return this; 
} 
public MikyouBackgroundSpan 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); 
//繪制文本背景 
super.draw(canvas, text, start, end, x, top, y, bottom, paint); 
//設置文本的顏色 
paint.setColor(mTextColor); 
//設置字體的大小 
paint.setTextSize(mTextSize); 
int mGapX = (getDrawable().getBounds().width() - maxWidth)/2; 
int mGapY= (getDrawable().getBounds().height() - maxHeight)/2; 
//繪制文本內容 
canvas.drawText(text.subSequence(start, end).toString(), x + mGapX , y - mGapY + maxHeight/3, paint); } 
}

三、樣式一的倒計時實現,樣式一指的是例如:12時36分27秒或者12:36:27就是將數值和時、分、秒或者":"分隔開,然後去自定義每塊數值(12 36 27)和間隔(時 分 秒 或 :)的樣式,包括給數值塊加背景和邊框。在MikyouCountDownTimer中的number數組中保存著[12 36 27]而nonumer數組中保存著[時 分 秒 ]或[ : :]d的間隔字符。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.text.SpannableString; 
import android.text.method.LinkMovementMethod; 
import android.text.style.ForegroundColorSpan; 
import android.text.style.ImageSpan; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class JDCountDownTimer extends MikyouCountDownTimer { 
private SpannableString mSpan; 
private Context mContext; 
private int mDrawableId; 
public JDCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
super(mContext, mGapTime, mTimePattern,mDrawableId); 
this.mContext = mContext; 
this.mDrawableId = mDrawableId; 
} 
/** 
* 重寫父類的initSpanData方法 
* 通過number數組得到每塊數值對應的自定義MikyouBackgroundSpan對象 
* 然後通過MikyouBackgroundSpan對象定義每塊數值的樣式包括背景,邊框,邊框圓角樣式,然後將這些對象加入到集合中去 
* 通過nonNumber數組得到每個間隔的ForegroundColorSpan對象 
* 然後通過這些對象就可以定義每個間隔塊的樣式,因為只定義了ForegroundColorSpan所以只能定義 
* 每個間隔塊的字體顏色,setmGapSpanColor方式也是供外部自由定制每個間隔的樣式 
* 實際上還可以定義其他的Span,同理實現也是很簡單的。 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
for (int i = 0; i<numbers.length;i++){ 
MikyouBackgroundSpan mBackSpan = new MikyouBackgroundSpan(mContext.getDrawable(mDrawableId), ImageSpan.ALIGN_BOTTOM); 
initBackSpanStyle(mBackSpan); 
mBackSpanList.add(mBackSpan); 
} 
for (int i= 0; i<nonNumbers.length;i++){ 
ForegroundColorSpan mGapSpan = new ForegroundColorSpan(mGapSpanColor); 
mTextColorSpanList.add(mGapSpan); 
} 
} 
/** 重寫父類的setBackgroundSpan方法 
* 我們知道設置Span的樣式主要是控制兩個變量start,end索引 
* 以確定設置start到end位置的字符串的子串的樣式 
* mGapLen = 1,表示一個間隔塊的長度, 
* 例如:12時36分27秒的"時","分","秒"的間隔長度 
* 所以通過遍歷Span集合,給字符串設置Span, 
* 通過分析不難得出每個數值塊的Span的start索引:start = i*numbers[i].length() + i*mGapLen; 
* end = start + numbers[i].length(); 
* */ 
@Override 
public void setBackgroundSpan(String timeStr) { 
super.setBackgroundSpan(timeStr); 
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(); 
TimerUtils.setContentSpan(mSpan,mBackSpanList.get(i),start,end); 
if (i < mTextColorSpanList.size()){//這裡為了就是防止12:36:27這種樣式,這種樣式間隔只有2個所以需要做判斷,防止數組越界 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),end,end + mGapLen); 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//此方法很重要需要調用,否則繪制出來的倒計時就是重疊的樣式 
mDateTv.setText(mSpan); 
} 
}

四、樣式二的倒計時實現,樣式二不同於樣式一的是例如:12時36分27秒或者12:36:27就是將每個數值和時、分、秒或者":"分隔開,然後去自定義每塊數值(1 2 3 6 2 7)和間隔(時 分 秒 或 :)的樣式,包括給數值塊加背景和邊框。在MikyouCountDownTimer中的vipNumber數組中保存著[1 2 3 6 2 7]而vipnonNumer數組中保存著[時 分 秒 ]或[ : :]d的間隔字符。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.text.SpannableString; 
import android.text.method.LinkMovementMethod; 
import android.text.style.ForegroundColorSpan; 
import android.text.style.ImageSpan; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
import java.util.ArrayList; 
import java.util.List; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class VIPCountDownTimer extends MikyouCountDownTimer { 
private SpannableString mSpan; 
private Context mContext; 
private int mDrawableId; 
private List<MikyouBackgroundSpan> mSpanList; 
private String[] vipNumbers; 
private char[] vipNonNumbers; 
public VIPCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
super(mContext, mGapTime, mTimePattern,mDrawableId); 
this.mContext = mContext; 
this.mDrawableId = mDrawableId; 
mSpanList = new ArrayList<>(); 
} 
/** 重寫父類的setBackgroundSpan方法 
* 我們知道設置Span的樣式主要是控制兩個變量start,end索引 
* 以確定設置start到end位置的字符串的子串的樣式,表示每個數字子串在整個字符串中的位置范圍 
* mGapLen = 1,表示一個間隔塊的長度, 
* 例如:12時36分27秒的"時","分","秒"的間隔長度 
* 所以通過遍歷Span集合,給字符串設置Span, 
* 通過分析不難得出每個數值塊的Span的start索引:start = i*numbers[i].length() + i*mGapLen; 
* end = start + numbers[i].length(); 
* */ 
@Override 
public void setBackgroundSpan(String timeStr) { 
int mGapLen = 1; 
mSpan = new SpannableString(timeStr); 
initSpanData(timeStr); 
int start = 0 ; 
int count =0; 
for (int i=0;i<vipNumbers.length;i++){ 
for (int j=start;j<start + vipNumbers[i].toCharArray().length;j++,count++){ 
TimerUtils.setContentSpan(mSpan,mSpanList.get(count),j,j+mGapLen); 
} 
//此時表示遍歷完了某一塊的數值,從而需要將此時該塊數值去更新start變量 
start = start + vipNumbers[i].toCharArray().length; 
if (i < nonNumbers.length){ 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),start,start+mGapLen); 
start = start +mGapLen;//如果是個間隔還得去加上每個間隔長度最後去更新start變量 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); 
mDateTv.setText(mSpan); 
} 
/** 
* 重寫父類的initSpanData方法 
* 通過number數組得到每塊數值對應的自定義MikyouBackgroundSpan對象 
* 然後通過MikyouBackgroundSpan對象定義每塊數值的樣式包括背景,邊框,邊框圓角樣式,然後將這些對象加入到集合中去 
* 通過nonNumber數組得到每個間隔的ForegroundColorSpan對象 
* 然後通過這些對象就可以定義每個間隔塊的樣式,因為只定義了ForegroundColorSpan所以只能定義 
* 每個間隔塊的字體顏色,setmGapSpanColor方式也是供外部自由定制每個間隔的樣式 
* 實際上還可以定義其他的Span,同理實現也是很簡單的。 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
vipNumbers = TimerUtils.getNumInTimerStr(timeStr);//得到每個數字注意不是每塊數值,並加入數組 
vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr);//得到每個間隔字符,並加入到數組 
for (int i=0;i<vipNumbers.length;i++){ 
for (int j=0;j<vipNumbers[i].toCharArray().length;j++){//因為需要得到每個數字所以還得遍歷每塊數值中的每個數字,所以需要二層循環 
MikyouBackgroundSpan mSpan = new MikyouBackgroundSpan(mContext.getDrawable(mDrawableId), ImageSpan.ALIGN_BOTTOM); 
initBackSpanStyle(mSpan); 
mSpanList.add(mSpan); 
} 
} 
for (int i= 0; i<vipNonNumbers.length;i++){ 
ForegroundColorSpan mGapSpan = new ForegroundColorSpan(mGapSpanColor); 
mTextColorSpanList.add(mGapSpan); 
} 
} 
}

四、TimerUtils管理類,主要是提供不同樣式的倒計時的對象給客戶端,所以這個類直接與客戶端建立關系,從而實現倒計時子類和基類對外界的隱藏體現了封裝性。

package com.mikyou.countdowntimer.utils; 
import android.content.Context; 
import android.graphics.Color; 
import android.text.SpannableString; 
import android.text.Spanned; 
import android.text.style.ForegroundColorSpan; 
import com.mikyou.countdowntimer.bean.JDCountDownTimer; 
import com.mikyou.countdowntimer.bean.MikyouCountDownTimer; 
import com.mikyou.countdowntimer.bean.VIPCountDownTimer; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class TimerUtils { 
public static final int JD_STYLE = 0; 
public static final int VIP_STYLE = 1; 
public static final int DEFAULT_STYLE = 3; 
public static final String TIME_STYLE_ONE = "HH:mm:ss"; 
public static final String TIME_STYLE_TWO = "HH時mm分ss秒"; 
public static final String TIME_STYLE_THREE = "dd天HH時mm分ss秒"; 
public static final String TIME_STYLE_FOUR = "dd天HH時mm分"; 
public static MikyouCountDownTimer getTimer(int style,Context mContext, long mGapTime, String mTimePattern, int mDrawableId){ 
MikyouCountDownTimer mCountDownTimer = null; 
switch (style){ 
case JD_STYLE: 
mCountDownTimer = new JDCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
case VIP_STYLE: 
mCountDownTimer = new VIPCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
case DEFAULT_STYLE: 
mCountDownTimer = new MikyouCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
} 
return mCountDownTimer; 
} 
//得到倒計時字符串中的數值塊部分 
public static String[] getNumInTimerStr(String mTimerStr){ 
return mTimerStr.split("[^\\d]"); 
} 
//得到倒計時中字符串中的非數值的字符串,並把數值過濾掉重新組合成一個字符串,並把字符串拆分字符數組,也就是保存倒計時中間的間隔 
public static char[] getNonNumInTimerStr(String mTimerStr){ 
return mTimerStr.replaceAll("\\d","").toCharArray(); 
} 
//設置字體顏色 
public static ForegroundColorSpan getTextColorSpan(String color){ 
ForegroundColorSpan mSpan = null; 
if (mSpan == null){ 
mSpan = new ForegroundColorSpan(Color.parseColor(color)); 
} 
return mSpan; 
} 
//設置內容的Span 
public static void setContentSpan(SpannableString mSpan, Object span, int start, 
int end) { 
mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
} 
}

現在我們就來測試下我們使用一個TextView實現的倒計時。

使用該倒計時非常簡單非常方便只需要一行代碼就能實現一個高仿京東和各種電商的APP的倒計時樣式。

package com.mikyou.countdowntimer; 
import android.graphics.Color; 
import android.os.Bundle; 
import android.support.v7.app.AppCompatActivity; 
import android.view.Gravity; 
import android.widget.LinearLayout; 
import android.widget.TextView; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
public class MainActivity extends AppCompatActivity { 
private LinearLayout parent; 
private int padding =10; 
private int textSize = 40; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
parent = (LinearLayout) findViewById(R.id.parent); 
//默認樣式倒計時每種樣式下又對應四種時間的格式 
/** 
* 默認+時間格式1:DEFAULT_STYLE <--> TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,0) 
.getmDateTv(); 
parent.addView(tv); 
setmLayoutParams(tv); 
/** 
* 默認+時間格式2:DEFAULT_STYLE <--> TIME_STYLE_TWO = "HH時mm分ss秒" 
* */ 
TextView tv1 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,0) 
.getmDateTv(); 
parent.addView(tv1); 
setmLayoutParams(tv1); 
/** 
* 默認+時間格式3:DEFAULT_STYLE <--> TIME_STYLE_THREE = "dd天HH時mm分ss秒" 
* */ 
TextView tv2 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,0) 
.getmDateTv(); 
parent.addView(tv2); 
setmLayoutParams(tv2); 
/** 
* 默認+時間格式4:DEFAULT_STYLE <--> TIME_STYLE_FOUR = "dd天HH時mm分" 
* */ 
TextView tv3 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,0) 
.getmDateTv(); 
parent.addView(tv3); 
setmLayoutParams(tv3); 
//樣式一倒計時,就是每塊數值和每個間隔分開的樣式,每種樣式下又對應四種時間的格式 
/** 
* 樣式一+時間格式1:JD_STYLE <--> TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv4= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,R.drawable.timer_shape) 
.setTimerPadding(10,10,10,10)//設置內間距 
.setTimerTextColor(Color.BLACK)//設置字體顏色 
.setTimerTextSize(40)//設置字體大小 
.setTimerGapColor(Color.BLACK)//設置間隔的顏色 
.getmDateTv();//拿到TextView對象 
parent.addView(tv4); 
setmLayoutParams(tv4); 
/** 
* 樣式一+時間格式2:JD_STYLE <--> TIME_STYLE_TWO = "HH時mm分ss秒" 
* */ 
TextView tv5= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,R.drawable.timer_shape2) 
.setTimerPadding(10,10,10,10) 
.setTimerTextColor(Color.WHITE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv5); 
setmLayoutParams(tv5); 
/** 
* 樣式一+時間格式3:JD_STYLE <-->TIME_STYLE_THREE = "dd天HH時mm分ss秒" 
* */ 
TextView tv6= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,R.drawable.timer_shape2) 
.setTimerPadding(10,10,10,10) 
.setTimerTextColor(Color.YELLOW) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv6); 
setmLayoutParams(tv6); 
/** 
* 樣式一+時間格式4:JD_STYLE <-->TIME_STYLE_FOUR = "dd天HH時mm分" 
* */ 
TextView tv7= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLUE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv7); 
setmLayoutParams(tv7); 
/** 
* 樣式二+時間格式1:VIP_STYLE <-->TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv8= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,R.drawable.timer_shape) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLACK) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv8); 
setmLayoutParams(tv8); 
/** 
* 樣式二+時間格式2:VIP_STYLE <-->TIME_STYLE_TWO = "HH時mm分ss秒" 
* */ 
TextView tv9= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.WHITE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv9); 
setmLayoutParams(tv9); 
/** 
* 樣式二+時間格式3:VIP_STYLE <-->TIME_STYLE_THREE = "dd天HH時mm分ss秒" 
* */ 
TextView tv10= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.YELLOW) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv10); 
setmLayoutParams(tv10); 
/** 
* 樣式二+時間格式4:VIP_STYLE <-->TIME_STYLE_FOUR = "dd天HH時mm分" 
* */ 
TextView tv11= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLUE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv11); 
setmLayoutParams(tv11); 
} 
private void setmLayoutParams(TextView tv) { 
tv.setGravity(Gravity.CENTER_HORIZONTAL); 
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tv.getLayoutParams(); 
params.setMargins(20,20,20,20); 
tv.setLayoutParams(params); 
} 
}

兩個drawable文件:

帶邊框樣式

<?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
android:shape="rectangle" 
> 
<corners android:radius="5px"/> 
<stroke android:color="#88000000" android:width="1dp"/> 
</shape>

帶背景和邊框樣式

<?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
android:shape="rectangle" 
> 
<corners android:radius="10px"/> 
<solid android:color="#000000"/> 
</shape>

現在就看看我們運行的成果吧。

看看運行結果還不錯吧,其實它的樣式還可以定義很多種主要看自己的創意和想法了,這個倒計時封裝如果還有什麼不足之處,請多多提出建議。但是現在使用還是蠻方便和簡單的,一行代碼就能就能解決。這個倒計時用到的地方還是蠻多的,大家有需要的話可以直接引入到自己的項目中。

Demo下載

以上所述是小編給大家介紹的Android中使用TextView實現高仿京東淘寶各種倒計時效果,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的,在此也非常感謝大家對本站網站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved