English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Implementazione di effetti countdown simili a those di Jingdong e Taobao utilizzando TextView in Android

Oggi vi porto un conto alla rovescia che utilizza solo un TextView per imitare le APP di e-commerce come Jingdong, Taobao, Vip.com e altre. Recentemente, la compagnia ha lavorato molto e non ho avuto tempo per organizzare tutto, oggi è raro riposarmi e voglio condividere questo con voi, sperando di imparare insieme e di复习are in futuro. Perché pensare di utilizzare un TextView? Poiché recentemente la compagnia ha fatto alcune ottimizzazioni, tra cui c'è uno stile di conto alla rovescia, il collega che ha sviluppato questo componente ha utilizzato più TextView messe insieme, il che rende il codice molto ingombrante. Pertanto, il project manager ha detto: Xiao Hong, questo lo lasci a te per ottimizzare e assicurati che abbia una certa estensibilità, all'epoca ero confuso e non sapevo da dove iniziare l'ottimizzazione. Poi ho controllato i conti alla rovescia delle APP come Jingdong, Ele.me, Vip.com e altre, e ho aperto l'interfaccia di livello degli sviluppatori per visualizzarli, ho scoperto che hanno un comune punto di forza: un View, senza utilizzare più TextView messe insieme. Credo che tutti sappiano l'effetto positivo di utilizzare solo un TextView rispetto a utilizzare più TextView messe insieme per implementare. Ecco alcuni esempi di interfaccia per vedere.


Vedendo questo, è naturale pensare a implementare una View personalizzata. Sì, una View personalizzata può effettivamente implementare tale effetto. Ma oggi non useremo una View personalizzata. Invece, useremo un TextView.

Poiché il project manager ha richiesto che il codice ottimizzato abbia una buona estensibilità, questa volta il design del codice ha aggiunto alcune conoscenze orientate agli oggetti. Ci sono alcune idee di progettazione e architettura proprie.

La logica di progettazione di questo demo:

          1. Scrivi una classe base per il conto alla rovescia per implementare le funzionalità più comuni e fondamentali del conto alla rovescia, senza alcun stile. Fai ereditare questa classe base dalla classe CountDownTimer e nel suo interno

Salva un oggetto TextView e visualizza i dati di conto alla rovescia in esso, quindi pubblica un metodo getmDateTv() che restituisce un oggetto TextView. Poi, è sufficiente ottenere l'oggetto TextView nella layout dell'interfaccia utente per mostrarlo. È molto conveniente.

          2. Then, for different styles of countdown, it is only necessary to write different subclasses to inherit the most ordinary countdown base class, and then rewrite the two methods of setting data and setting style, then you can add different styles to the most ordinary countdown. Next time if you need to expand a new countdown style, you do not need to change the code of other classes, just write a derived class of a normal countdown and rewrite the two methods, making the extensibility more flexible.

          3. Then, through a TimerUtils management class, to centrally bear the pressure of subclasses and superclass, so that the required implementation functions of subclasses and superclass are distributed to the TimerUtils class, and the TimerUtils management class is the only class that interacts with the client, such as obtaining the countdown object and obtaining the TextView object of the countdown all through this management class allocation, avoiding the client directly interacting with the base class and subclasses of countdown. Thus, the encapsulation and concealment of the class are reflected.

Below is the simple UML class diagram of this Demo design:


Through the above analysis, let's see what knowledge points are needed for the implementation of this Demo.

 1. Usage of CountDownTimer class.

    2. Usage of SpannableString.

    3. Encapsulation of MikyouCountDownTimer.

    4. Custom implementation of MikyouBackgroundSpan.

Through the above analysis, we first need to review the knowledge related to CountDownTimer. CountDownTimer is a very simple class, and we can see its source code to understand its usage naturally.

CountDownTimer is an abstract class.

// 
// 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(); 
}

As can be seen, the total duration of the countdown is millisFuture, and the countDownInterVal interval step is default 1000ms, so the data is initialized through the constructor, and then a callback method onTick needs to be overridden, one of the parameters of which is the remaining time in milliseconds after each corresponding step. Then we just need to format the time in milliseconds every 1000ms in the onTick method to get the countdown in the corresponding time format, which is the basic countdown style implemented. The countdown format is formatted using the formatDuration method of the DurationFormatUtils class in the common lang package of apache, by passing a time format it will automatically convert the countdown to the corresponding mTimePattern style (HH:mm:ss or dd days HH hours mm minutes ss seconds).

Secondly, let's review the usage of SpannableString.

In Android, EditText is used for editing text, TextView is used for displaying text, but sometimes we need to set styles and other aspects of the text. Android provides the SpannableString class to process specified text.

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("grassetto corsivo"); 
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC); 
spanString.setSpan(span, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

4) RelativeSizeSpan dimensione relativa

private void setRelativeFontSpan() { 
SpannableString spanString = new SpannableString("dimensione relativa del carattere"); 
spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

5) TypefaceSpan fonte di testo

private void setTypefaceSpan() { 
SpannableString spanString = new SpannableString("fonte di testo"); 
spanString.setSpan(new TypefaceSpan("monospace"), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanText); 
}

6) URLSpan collegamento ipertestuale di testo

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

7) ImageSpan immagine

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 il testo ha un evento di clic

private TextView textView; 
textView = (TextView)this.findViewById(R.id.textView); 
String text = "Visualizza 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); 
} 
// Indica che l'intero lunghezza del testo è efficace per attivare questo evento 
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 
textView.setMovementMethod(LinkMovementMethod.getInstance());

9) UnderlineSpan sottolineato

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

10) StrikethroughSpan

linea through

private void addStrikeSpan() { 
SpannableString spannableString = 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); 
}

Bene, attraverso i punti chiave di revisione sopra menzionati, possiamo ora iniziare a implementare il demo, quindi andiamo a passo passo a implementare il nostro countdown.

1. Scrivere una classe base MikyouCountDownTimer, farla ereditare dalla classe CountDownTimer e rendere pubblici i metodi initSpanData e setBackgroundSpan per l'uso dei sottoclassi di altri stili di countdown, che può implementare la funzione di countdown di base.

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;//oggetto contesto passato come argomento 
protected TextView mDateTv;//un TextView per implementare il countdown 
private long mGapTime;//intervallo di tempo impostato in input, durata totale del countdown 
private long mCount = 1000;//passo del countdown, di solito 1000 rappresenta un salto ogni 1s 
private String mTimePattern = "HH:mm:ss";//timePattern il formato del tempo passato come argomento come: HH:mm:ss HH ore mm minuti ss secondi dd giorni HH ore mm minuti ss secondi 
private String mTimeStr; 
protected List<MikyouBackgroundSpan> mBackSpanList; 
protected List<ForegroundColorSpan> mTextColorSpanList; 
private int mDrawableId; 
private boolean flag = false;//imposta il segno flag, utilizzato per controllare che i dati Span iniziali vengano impostati una volta 
protected String[] numbers;//questo array serve per salvare i valori numerici di giorno, ora, minuto, secondo divisi per il countdown 
protected char[] nonNumbers;//salva l'intervallo tra giorno, ora, minuto, secondo ("giorno", "ora", "minuto", "secondo" o ":") 
//usato per l'interno del countdown, dimensione del carattere, colore del carattere, colore dell'intervallo di tempo del countdown 
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; // Durata totale del timer 
this.mCount = mCount; // Passo di contggio del timer ogni volta, predefinito a 1000 
this.mDrawableId = mDrawableId; // Utilizzato per impostare l'id del drawable di sfondo 
this.mTimePattern = mTimePattern; // Formato dell'orario: ad esempio HH:mm:ss o dd giorni HH ore mm minuti ss secondi 
mBackSpanList = new ArrayList<>(); 
mTextColorSpanList = new ArrayList<>(); 
mDateTv = new TextView(mContext, null); 
} 
//Pubblica questi metodi di configurazione del layout del timer, disponibili per chiamate esterne, per personalizzare flessibilmente lo stile del timer 
public MikyouCountDownTimer setTimerTextSize(int textSize) { 
this.mSpanTextSize = textSize; 
ritorna this; 
} 
public MikyouCountDownTimer setTimerPadding(int left, int top, int right, int bottom) { 
this.mSpanPaddingLeft = left; 
this.mSpanPaddingBottom = bottom; 
this.mSpanPaddingRight = right; 
this.mSpanPaddingTop = top; 
ritorna this; 
} 
public MikyouCountDownTimer setTimerTextColor(int color){ 
this.mSpanTextColor = color; 
ritorna this; 
} 
public MikyouCountDownTimer setTimerGapColor(int color){ 
this.mGapSpanColor = color; 
ritorna this; 
} 
//Set the style of the Span for countdown, publish it for implementation by subclasses 
public void setBackgroundSpan(String timeStr) { 
if (!flag){ 
initSpanData(timeStr); 
flag = true; 
} 
mDateTv.setText(timeStr); 
} 
//Set the data of the Span for countdown, publish it for implementation by subclasses 
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); 
//This is the formatDuration method in the DurationFormatUtils class of the common.lang package in Apache, through the input 
//Un formato di data e ora trasformerà automaticamente il conto alla rovescia nel corrispondente stile mTimePattern (HH:mm:ss o dd giorni HH ore mm minuti ss) 
setBackgroundSpan(mTimeStr); 
} 
} 
@Override 
public void onFinish() { 
mDateTv.setText("Conto alla rovescia terminato"); 
} 
//Serve per restituire l'oggetto TextView che mostra il conto alla rovescia 
public TextView getmDateTv() { 
startTimer(); 
return mDateTv; 
} 
public void cancelTimer(){ 
this.cancel(); 
} 
public void startTimer(){ 
this.start(); 
} 
public String getmTimeStr() { 
return mTimeStr; 
} 
}

La classe TimerUtils serve per salvare diversi formati di conto alla rovescia, ad esempio HH:mm:ss, HH ore mm minuti ss, dd giorni HH ore mm minuti ss ecc. Ora possiamo guardare lo stile di base semplice.

Secondo, creare una classe personalizzata MikyouBackgroundSpan che eredita ImageSpan, questa classe è molto importante e serve per aggiungere stili al TextView di conto alla rovescia, perché è possibile utilizzare un TextView per farlo?

Non dimenticare che c'è una classe molto potente chiamata SpannableString, questa classe permette di impostare lo stile di ciascun carattere di una stringa, molti stili. Infine, attraverso il metodo setSpan del TextView si può passare
Un oggetto SpannableString completato. Ma perché c'è bisogno di uno Span personalizzato? Questo è perché è strano che in Android ci siano così tanti stili Span e nessuno di loro permetta di impostare direttamente un file di oggetto Drawable, quindi ho cercato molto su Internet e non ho trovato nulla, infine su stackOverFlow ho trovato una soluzione proposta da un straniero, che è sovrascrivere ImageSpan e poi è possibile impostare il file 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 allineamentoVerticale) { 
super(d, allineamentoVerticale); 
mTextBound = new Rect(); 
} 
public MikyouBackgroundSpan impostaColoreTestoTimer(int mTextColor) { 
this.mTextColor = mTextColor; 
ritorna this; 
} 
public MikyouBackgroundSpan impostaDimensioneTestoTimer(int dimensioneTesto){ 
this.mTextSize = dimensioneTesto; 
ritorna this; 
} 
public MikyouBackgroundSpan impostaPaddingTimer(int sinistra, int alto, int destra, int basso){ 
this.mPaddingLeft = sinistra; 
this.mPaddingRight = destra; 
this.mPaddingBottom = basso; 
this.mPaddingTop = alto; 
ritorna this; 
} 
@Override 
public void disegna(Canvas canvas,(CharSequence testo, int inizio, int fine, float x, int alto, int y, int basso, Paint pittura) { 
// Disegna lo sfondo del contenuto del testo 
paint.setTextSize(mTextSize); 
// Misura la larghezza e l'altezza del testo, tramite mTextBound 
paint.getTextBounds(text.toString(), start, end, mTextBound); 
// Imposta la larghezza e l'altezza dello sfondo del testo, i parametri passati sono left, top, right, bottom 
maxWidth = maxWidth < mTextBound.width() ? mTextBound.width() : maxWidth; 
maxHeight = maxHeight < mTextBound.height() ? mTextBound.height() : maxHeight; 
// Imposta l'altezza e la larghezza massima per prevenire il ridisegno durante la transizione dei numeri del countdown, che potrebbe causare il tremolo della larghezza e dell'altezza del bordo del countdown 
// Quindi ottieni sempre l'altezza e la larghezza massima, non l'altezza e la larghezza misurate ogni volta 
getDrawable().setBounds(0, 0, maxWidth + mPaddingLeft + mPaddingRight, mPaddingTop + mPaddingBottom + maxHeight); 
// Disegna lo sfondo del testo 
super.draw(canvas, text, start, end, x, top, y, bottom, paint); 
// Imposta il colore del testo 
paint.setColor(mTextColor); 
// Imposta la dimensione del carattere del testo 
paint.setTextSize(mTextSize); 
int mGapX = (getDrawable().getBounds().width() - maxWidth) / 2; 
int mGapY = (getDrawable().getBounds().height() - maxHeight) / 2; 
// Disegna il contenuto del testo 
canvas.drawText(text.subSequence(start, end).toString(), x + mGapX, y - mGapY + maxHeight/3, paint); 
}

Terza parte: implementazione del timer a scadenza di stile uno, lo stile uno si riferisce a esempi come 12 ore 36 minuti 27 secondi o 12:36:27, che separa i numeri e le ore, minuti e secondi o ':' e poi personalizza lo stile di ogni sezione numerica (12 36 27) e l'intervallo (ore, minuti, secondi o ':') inclusi lo stile di sfondo e i bordi dei segmenti numerici. Nell'array number di MikyouCountDownTimer vengono salvati [12 36 27] e nell'array nonumer vengono salvati i caratteri di intervallo [ore, minuti, secondi] o [ : :].

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; 
} 
/** 
* Override del metodo initSpanData della classe padre 
* È possibile ottenere l'oggetto MikyouBackgroundSpan personalizzato corrispondente a ogni pezzo di valore attraverso l'array number 
* Poi è possibile definire lo stile di ogni pezzo di valore attraverso l'oggetto MikyouBackgroundSpan, inclusi sfondo, bordo, stile di arrotondamento dei bordi, e aggiungere questi oggetti alla raccolta 
* È possibile ottenere l'oggetto ForegroundColorSpan per ogni intervallo attraverso l'array nonNumber 
* Poi è possibile definire lo stile di ogni intervallo di spaziatura attraverso questi oggetti, perché è stato definito solo ForegroundColorSpan, quindi è possibile definire solo 
* La colorazione del testo di ogni intervallo di setmGapSpanColor è anche disponibile per la personalizzazione esterna dello stile di ogni intervallo 
* È possibile definire altri Span, e l'implementazione è anch'essa molto semplice. 
* */ 
@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); 
} 
} 
/** Riscrivi il metodo setBackgroundSpan del padre 
* Sappiamo che l'impostazione dello stile Span principalmente controlla due variabili: start, end index 
* per determinare lo stile della sottostringa tra le posizioni start e end 
* mGapLen = 1 indica la lunghezza di un blocco di intervallo, 
* Ad esempio: "12 ore 36 minuti 27 secondi" la lunghezza dell'intervallo tra "ora", "minuto" e "secondo" 
* Quindi esaminando la raccolta di Span, è possibile impostare lo Span sulla stringa, 
* È facile dedurre che l'indice di partenza del Span di ogni blocco di numeri è: 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()){//per prevenire uno stile come 12:36:27, questo stile ha un intervallo di soli 2, quindi è necessario fare una verifica, per prevenire l'overflow dell'array 
TimerUtils.setContentSpan(mSpan, mTextColorSpanList.get(i), end, end + mGapLen); 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); // Questo metodo è molto importante da chiamare, altrimenti il countdown disegnato sarà sovrapposto 
mDateTv.setText(mSpan); 
} 
}

Quarto, l'implementazione del countdown in stile due, che si differenzia dallo stile uno nel senso che ad esempio 12 ore 36 minuti 27 secondi o 12:36:27 significa separare ogni valore e ora, minuto, secondo o ":" e poi personalizzare lo stile di ogni sezione numerica (1 2 3 6 2 7) e gli intervalli (ora spaziale minuto secondo o ":") inclusi dare sfondo e bordo ai segmenti numerici. Nell'array vipNumber di MikyouCountDownTimer è salvato [1 2 3 6 2 7] mentre nell'array vipnonNumer sono salvati [ora minuto secondo] o [ : :]d i caratteri di intervallo.

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<>(); 
} 
/** Riscrivi il metodo setBackgroundSpan del padre 
* Sappiamo che l'impostazione dello stile Span principalmente controlla due variabili: start, end index 
* per determinare lo stile della sottostringa da start a end, rappresentando la posizione di ciascun sottostringa numerica nella stringa intera 
* mGapLen = 1 indica la lunghezza di un blocco di intervallo, 
* Ad esempio: "12 ore 36 minuti 27 secondi" la lunghezza dell'intervallo tra "ora", "minuto" e "secondo" 
* Quindi esaminando la raccolta di Span, è possibile impostare lo Span sulla stringa, 
* È facile dedurre che l'indice di partenza del Span di ogni blocco di numeri è: 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; 
per (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); 
} 
//Allora rappresenta il completamento dell'iterazione di un blocco di numeri, quindi è necessario aggiornare la variabile start con il valore di questo blocco di numeri 
start = start + vipNumbers[i].toCharArray().length; 
if (i < nonNumbers.length){ 
TimerUtils.setContentSpan(mSpan, mTextColorSpanList.get(i), start, start+mGapLen); 
start = start +mGapLen; // Se è un intervallo, è necessario aggiungere la lunghezza di ogni intervallo e aggiornare la variabile start alla fine 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); 
mDateTv.setText(mSpan); 
} 
/** 
* Override del metodo initSpanData della classe padre 
* È possibile ottenere l'oggetto MikyouBackgroundSpan personalizzato corrispondente a ogni pezzo di valore attraverso l'array number 
* Poi è possibile definire lo stile di ogni pezzo di valore attraverso l'oggetto MikyouBackgroundSpan, inclusi sfondo, bordo, stile di arrotondamento dei bordi, e aggiungere questi oggetti alla raccolta 
* È possibile ottenere l'oggetto ForegroundColorSpan per ogni intervallo attraverso l'array nonNumber 
* Poi è possibile definire lo stile di ogni intervallo di spaziatura attraverso questi oggetti, perché è stato definito solo ForegroundColorSpan, quindi è possibile definire solo 
* La colorazione del testo di ogni intervallo di setmGapSpanColor è anche disponibile per la personalizzazione esterna dello stile di ogni intervallo 
* È possibile definire altri Span, e l'implementazione è anch'essa molto semplice. 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
vipNumbers = TimerUtils.getNumInTimerStr(timeStr); // Ottenere ogni numero, non ogni pezzo di valore, e aggiungerlo all'array 
vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr); // Ottenere ogni intervallo di carattere e aggiungerlo all'array 
per (int i=0;i<vipNumbers.length;i++){ 
per ottenere ogni numero, è necessario percorrere ogni numero di ogni pezzo di valore, quindi è necessario un ciclo di due livelli; 
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); 
} 
} 
}

Quattro, la classe di gestione TimerUtils, fornisce oggetti di conteggio alla rovescia con stili diversi al client, quindi questa classe si stabilisce direttamente in relazione con il client, realizzando così l'opacità della classe figlia e della classe base verso l'esterno e mostrando l'opacità.

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; 
} 
// ottiene la parte numerica della stringa del countdown 
public static String[] getNumInTimerStr(String mTimerStr){ 
return mTimerStr.split("[^\\d]"); 
} 
// ottiene la stringa non numerica nella stringa del countdown e filtra i numeri per ricombinarli in una stringa, quindi divide la stringa in un array di caratteri, ovvero salvando gli intervalli tra il countdown 
public static char[] getNonNumInTimerStr(String mTimerStr){ 
return mTimerStr.replaceAll("\\d", "").toCharArray(); 
} 
//Impostare il colore del testo 
public static ForegroundColorSpan getTextColorSpan(String color){ 
ForegroundColorSpan mSpan = null; 
if (mSpan == null){ 
mSpan = new ForegroundColorSpan(Color.parseColor(color)); 
} 
return mSpan; 
} 
//Impostare il Span del contenuto 
public static void setContentSpan(SpannableString mSpan, Object span, int start, 
int end) { 
mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
} 
}

Ora testiamo il contatore a ritardo implementato con un TextView.

L'uso di questo contatore a ritardo è molto semplice e comodo, è sufficiente una riga di codice per realizzare uno stile di contatore a ritardo simile a JD.com e altri app di e-commerce.

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); 
// Ogni stile di conto alla rovescia predefinito corrisponde a quattro formati di tempo per ogni stile 
/** 
* Formato di tempo predefinito + 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); 
/** 
* Formato di tempo predefinito + 2: DEFAULT_STYLE <--> TIME_STYLE_TWO = "HH ore mm minuti ss secondi" 
* */ 
TextView tv1 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE, this, 120000000, TimerUtils.TIME_STYLE_TWO, 0) 
.getmDateTv(); 
parent.addView(tv1); 
setmLayoutParams(tv1); 
/** 
* Formato di tempo predefinito + 3: DEFAULT_STYLE <--> TIME_STYLE_THREE = "dd giorni HH ore mm minuti ss secondi" 
* */ 
TextView tv2 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE, this, 120000000, TimerUtils.TIME_STYLE_THREE, 0) 
.getmDateTv(); 
parent.addView(tv2); 
setmLayoutParams(tv2); 
/** 
* Formato di tempo predefinito + 4: DEFAULT_STYLE <--> TIME_STYLE_FOUR = "dd giorni HH ore mm minuti" 
* */ 
TextView tv3 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE, this, 120000000, TimerUtils.TIME_STYLE_FOUR, 0) 
.getmDateTv(); 
parent.addView(tv3); 
setmLayoutParams(tv3); 
// Stile 1 countdown, è lo stile in cui ogni numero e ogni intervallo sono separati, e c'è anche un formato temporale per ogni stile 
/** 
* Stile 1 + formato temporale 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) // Impostare il margine interno 
.setTimerTextColor(Color.BLACK) // Impostare il colore del testo 
.setTimerTextSize(40) // Impostare la dimensione del testo 
.setTimerGapColor(Color.BLACK) // Impostare il colore dell'interruzione 
.getmDateTv(); // Ottenere l'oggetto TextView 
parent.addView(tv4); 
setmLayoutParams(tv4); 
/** 
* Stile 1 + formato temporale 2: JD_STYLE <--> TIME_STYLE_TWO = "HH ore mm minuti ss secondi" 
* */ 
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); 
/** 
* Stile 1 + formato temporale 3: JD_STYLE <--> TIME_STYLE_THREE = "dd giorni HH ore mm minuti ss secondi" 
* */ 
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); 
/** 
* Stile uno + formato temporale 4: JD_STYLE <--> TIME_STYLE_FOUR = "dd giorni HH ore mm minuti" 
* */ 
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); 
/** 
* Stile due + formato temporale 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); 
/** 
* Stile due + formato temporale 2: VIP_STYLE <--> TIME_STYLE_TWO = "HH ore mm minuti ss secondi" 
* */ 
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); 
/** 
* Stile due + formato temporale 3: VIP_STYLE <--> TIME_STYLE_THREE = "dd giorni HH ore mm minuti ss secondi" 
* */ 
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); 
/** 
* Stile due + formato di ora 4: VIP_STYLE <--> TIME_STYLE_FOUR = "dd giorni HH ore mm minuti" 
* */ 
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); 
} 
}

Due file drawable:

Stile con bordo

<?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>

Stile con sfondo e bordo

<?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>

Ora vediamo il risultato del nostro runtime.

Guarda i risultati del runtime, sembra che il design sia abbastanza versatile e può essere definito in molti modi, principalmente a seconda della tua creatività e idee. Se questa implementazione del countdown ha qualche lacuna, non esitare a farlo sapere con suggerimenti. Tuttavia, è molto conveniente e semplice da usare, risolvendo tutto con una singola riga di codice. Questo countdown è molto utilizzato, quindi se hai bisogno, puoi semplicemente aggiungerlo al tuo progetto.

Download Demo

Come ha spiegato l'editor, ecco come implementare l'effetto countdown di京东淘宝in Android utilizzando TextView, speriamo che sia utile a tutti. Se avete domande, lasciate un commento e l'editor risponderà prontamente. In questo senso, ringraziamo anche tutti i sostenitori del sito tutorial di urla!

Dichiarazione: Il contenuto di questo articolo è stato tratto da Internet, il copyright spetta ai rispettivi proprietari. Il contenuto è stato contribuito volontariamente dagli utenti di Internet e caricato autonomamente. Questo sito non detiene i diritti di proprietà e non ha effettuato una modifica editoriale, né assume responsabilità legali correlate. Se trovi contenuti che violano i diritti d'autore, ti preghiamo di inviare una e-mail a notice#oldtoolbag.com (sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata la veridicità, questo sito eliminerà immediatamente i contenuti contestati.

Ti potrebbe interessare