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

Implementação de efeitos de contagem regressiva semelhantes ao Jingdong e Taobao usando TextView no Android

Hoje, vou trazer para vocês como usar apenas um TextView para implementar um cronômetro de atividades de lojas online como o JD, Taobao, Vipshop e outros. Recentemente, a empresa tem trabalhado em horário extra e não tive tempo de organizar, hoje, graças ao tempo de folga, quero compartilhar isso com vocês, apenas para aprender juntos e para revisar posteriormente. Por que usar um TextView? Porque recentemente, a empresa está fazendo algumas otimizações, e uma delas é o estilo de cronômetro. O colega que desenvolveu o widget anterior usou vários TextViews empilhados, resultando em código redundantemente grande. Portanto, o gerente de projeto disse: 'Xiaohong, isso vai para você para otimizar, e ainda assim, tem que garantir uma certa flexibilidade'. Naquele momento, fiquei confuso sobre como começar a otimização. Então, eu olhei para os cronômetros de várias APPs como o JD, Ele.me, Vipshop e outros, e abri o painel de desenvolvedores para exibir a hierarquia, descobri que todos têm um ponto em comum: um View, sem usar vários TextViews empilhados. Acredito que todos sabem os benefícios de usar apenas um TextView em vez de empilhar vários TextViews. Abaixo, vamos ver alguns painéis para entender.


Ao ver isso, naturalmente, todos pensam em implementar uma View personalizada. Claro, uma View personalizada pode realmente implementar tal efeito. Mas hoje não vamos usar uma View personalizada. Vamos usar um TextView.

Devido ao requisito do gerente de projeto de que o código otimizado tenha flexibilidade, foi adicionado alguns conhecimentos de orientação a objetos ao design do código. Existem algumas próprias ideias de design e arquitetura.

A abordagem de design deste demo é:

          1. Escreva uma classe base para o cronômetro que implementa as funções mais básicas e comuns do cronômetro, sem estilo, faça essa classe herdar a classe CountDownTimer e no método da classe base

Salve uma instância de TextView e exiba os dados do cronômetro em cada intervalo na TextView, em seguida, publique um método getmDateTv() que retorna uma instância de TextView. Em seguida, você pode simplesmente obter essa instância de TextView no layout da interface de exibição. Muito conveniente.

          2, em seguida, diferentes estilos de countdown, basta escrever diferentes subclasses para herdar a classe base de countdown mais comum, então sobrescrever os dois métodos de configuração de dados e configuração de estilo, então pode adicionar diferentes estilos ao countdown mais comum. Da próxima vez que precisar expandir novos estilos de countdown, não é necessário alterar o código de outras classes, basta escrever uma subclass comum de countdown e sobrescrever os dois métodos para tornar a expansibilidade mais flexível.

          3, em seguida, através de uma classe de gerenciamento TimerUtils, assumir a pressão concentrada entre subclasses e subclasses, dividindo as funções necessárias das subclasses e subclasses para a classe TimerUtils, e a classe de gerenciamento TimerUtils é a única classe que lida com o cliente, por exemplo, para obter o objeto countdown e obter o objeto TextView de countdown, todos são distribuídos através dessa classe de gerenciamento, evitando que o cliente lide diretamente com a classe base e subclasses de countdown. Assim, a encapsulamento e ocultamento da classe são evidenciados.

A seguir, podemos ver o diagrama de classes UML simplesmente projetado para este Demo:


Através da análise acima, vamos ver quais pontos de conhecimento precisam ser usados na implementação deste Demo.

 1Uso da classe CountDownTimer.

    2, uso de SpannableString.

    3, encapsulamento de MikyouCountDownTimer.

    4, implementação personalizada de MikyouBackgroundSpan.

Um, através do análise acima, devemos revisar primeiramente o conhecimento sobre CountDownTimer, CountDownTimer é uma classe muito simples, podemos ver seu código-fonte, e seu uso naturalmente saberá.

CountDownTimer é uma classe abstrata.

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

Pode ser visto que a duração total da contagem regressiva é millisFuture, e o intervalo de contagem regressiva countDownInterVal é o passo padrão1000ms, portanto, todos os dados são inicializados através de seu construtor, e, em seguida, é necessário sobrescrever um método de callback onTick, cujo parâmetro é o tempo restante em milissegundos a cada passo correspondente. Em seguida, basta chamar o método onTick para obter1000ms, formatando o tempo em milissegundos para o formato de tempo correspondente, obtemos o contagem regressiva. Isso é o básico da contagem regressiva estilizada. O formato da contagem regressiva é formatado usando o método formatDuration da classe DurationFormatUtils do pacote common.lang do apache, passando um formato de tempo que automaticamente converte a contagem regressiva para o estilo mTimePattern (HH:mm:ss ou dd dias HH horas mm minutos ss segundos).

Revisemos a utilização de SpannableString.

No Android, EditText é usado para editar texto, TextView para exibir texto, mas às vezes precisamos configurar estilos e outros aspectos do texto. O Android oferece a classe SpannableString para manipular textos específicos.

1) ForegroundColorSpan cor do texto

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

2) BackgroundColorSpan cor de fundo do texto

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

3) StyleSpan estilo de fonte: negrito, itálico, etc

private void setStyleSpan() { 
SpannableString spanString = new SpannableString("negrito itálico"); 
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC); 
spanString.setSpan(span, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

4) RelativeSizeSpan tamanho relativo

private void setRelativeFontSpan() { 
SpannableString spanString = new SpannableString("tamanho relativo da fonte"); 
spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

5) TypefaceSpan texto de fonte

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

6) URLSpan link de texto

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

7) ImageSpan imagem

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

8ClickableSpan texto com evento de clique

private TextView textView; 
textView = (TextView)this.findViewById(R.id.textView); 
String text = "atividade de exibição"; 
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 que todo o comprimento do texto é válido para acionar este evento 
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 
textView.setMovementMethod(LinkMovementMethod.getInstance());

9) UnderlineSpan sublinhado

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

10) StrikethroughSpan

linhagem deletada

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

11) SuggestionSpan

Equivalente a um marcador de posição

12) MaskFilterSpan

Efeitos de decoração, como borrão (BlurMaskFilter), relevo (EmbossMaskFilter)

13) RasterizerSpan

Efeito raster

14) AbsoluteSizeSpan

Tamanho absoluto (fonte de texto)

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

15) DynamicDrawableSpan configuração de imagem, alinhamento baseado na linha de texto ou no fundo.

16) TextAppearanceSpan

Aparência de texto (inclusive tipo de letra, tamanho, estilo e cor)

private void setTextAppearanceSpan() { 
SpannableString spanString = new SpannableString("Aparência de texto"); 
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(this, android.R.style.TextAppearance_Medium); 
spanString.setSpan(textAppearanceSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

Bom, através dos pontos de revisão acima, podemos agora realmente começar a implementação do demo, e vamos juntos encapsular passo a passo o nosso temporizador.

Um, escreva uma classe base MikyouCountDownTimer, faça ela herdar a classe CountDownTimer e forneça os métodos initSpanData e setBackgroundSpan para que os subclasses de estilo de contagem regressiva possam usá-los, ela pode implementar as funções básicas de contagem regressiva.

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;//O tempo total do temporizador invertido 
this.mCount = mCount;//O passo do temporizador invertido a cada vez, o padrão é1000 
this.mDrawableId= mDrawableId;//Usado para definir o id do drawable de fundo 
this.mTimePattern = mTimePattern;//O formato do tempo: como HH:mm:ss ou dd dia HH hora mm minuto ss segundo, etc. 
mBackSpanList = new ArrayList<>(); 
mTextColorSpanList = new ArrayList<>(); 
mDateTv = new TextView(mContext,null); 
} 
//Divulgar esses métodos de configuração do estilo do temporizador invertido, para chamadas externas, permitindo personalizar flexivelmente o estilo do temporizador invertido 
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; 
} 
//Definir o estilo do Span do temporizador invertido, divulgando para que as subclasses implementem 
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; 
} 
}

A classe TimerUtils é usada para salvar diferentes formatos de contagem regressiva, por exemplo, HH:mm:ss, HH horas mm minutos ss segundos, dd dias HH horas mm minutos ss segundos, etc. Agora podemos ver os estilos básicos simples.

Segundo, personalize o MikyouBackgroundSpan herdando ImageSpan, esta classe é muito importante para adicionar estilos ao TextView de contagem regressiva, por que podemos usar um TextView para implementar isso?

Não se esqueça de que há uma classe muito poderosa chamada SpannableString, que permite configurar o estilo de cada caractere em um trecho de string, muitos estilos. Finalmente, através do método setSpan do TextView, podemos passar
Um objeto SpannableString conclui a configuração. Mas por que precisamos de um Span personalizado? Isso é porque é estranho que muitos Span de estilo no android não permitam a configuração de um objeto drawable diretamente, então procurei muito na internet e não encontrei nada, até que encontrei no stackOverFlow um estrangeiro que ofereceu uma solução, que é sobrescrever o ImageSpan para que possamos implementar a configuração do arquivo 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) { 
//Desenhar o fundo do conteúdo do texto 
paint.setTextSize(mTextSize); 
//Medir a largura e altura do texto, obter através de mTextBound 
paint.getTextBounds(text.toString(), start, end, mTextBound); 
//Definir a largura e altura do fundo do texto, os parâmetros passados são left, top, right e bottom 
maxWidth = maxWidth < mTextBound.width() ?63; mTextBound.width() : maxWidth; 
maxHeight = maxHeight < mTextBound.height() ?63; mTextBound.height() : maxHeight; 
//Definir a largura e altura máximas é para evitar que o redesenho durante a troca de números no cronômetro cause, resultando em tremores na largura e altura da borda do cronômetro 
// Therefore, each time the maximum height and width are obtained instead of measuring the height and width each time 
getDrawable().setBounds(0, 0, maxWidth+mPaddingLeft+mPaddingRight, mPaddingTop+mPaddingBottom+maxHeight); 
//Draw text background 
super.draw(canvas, text, start, end, x, top, y, bottom, paint); 
//Set text color 
paint.setColor(mTextColor); 
//Set font size 
paint.setTextSize(mTextSize); 
int mGapX = (getDrawable().getBounds().width() - maxWidth)/2; 
int mGapY = (getDrawable().getBounds().height() - maxHeight)/2; 
//Draw text content 
canvas.drawText(text.subSequence(start, end).toString(), x + mGapX, y - mGapY + maxHeight/3, paint); } 
}

Three, the countdown implementation of style one, style one refers to, for example:12horas36minutos27segundos ou12:36:27is to separate the number and hour, minute, second or ":", then customize each number block (12 36 27) and the style of the separator (hour minute second or :), including adding background and border to the number blocks. In the number array of MikyouCountDownTimer, the [12 36 27] whereas the nonumer array stores the [hour minute second] or [ : : ]d separator characters.

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; 
} 
/** 
* Sobrescrever o método initSpanData da classe pai 
* Obter o objeto MikyouBackgroundSpan personalizado correspondente ao bloco de valores através do array number 
* Depois, através do objeto MikyouBackgroundSpan, definir o estilo de cada bloco de valores, incluindo fundo, borda, estilo de borda arredondada, e adicionar esses objetos à coleção 
* Obter o objeto ForegroundColorSpan de cada separação através do array nonNumber 
* Depois, através desses objetos, é possível definir o estilo de cada bloco de separação, porque só foi definido ForegroundColorSpan, então só pode ser definido 
* A cor do texto de cada bloco de separação, a maneira de setmGapSpanColor também fornece a personalização de estilo de cada separação externa 
* Na verdade, ainda é possível definir outros Span, a implementação também é很简单. 
* */ 
@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); 
} 
} 
/** Sobrescrevendo o método setBackgroundSpan da superclasse 
* Sabemos que a configuração do estilo Span é principalmente controlar duas variáveis, índices start e end 
* para determinar o estilo da substring da string da posição start até end 
* mGapLen = 1,representa o comprimento de um bloco de intervalo, 
* por exemplo:12horas36minutos27A largura do intervalo entre "horas", "minutos" e "segundos" 
* Portanto, ao percorrer a coleção de Span, definimos Span para a string, 
* A análise mostra claramente que cada Span do bloco de número tem o índice de início: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()){//Aqui para evitar12:36:27Este estilo, a separação deste estilo é apenas de 2, então é necessário fazer uma verificação para evitar o desbordamento do array 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),end,end + mGapLen); 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//Este método é muito importante e deve ser chamado, senão a contagem regressiva desenhada será estilizada de forma sobreposta 
mDateTv.setText(mSpan); 
} 
}

Quarto, a implementação do estilo de contagem regressiva do estilo dois, o estilo dois é diferente do estilo um em que, por exemplo:12horas36minutos27segundos ou12:36:27isto é, separar cada número e horas, minutos, segundos ou ":", e personalizar cada bloco de número(1 2 3 6 2 7) e o estilo de separação (horas minutos segundos ou :), incluindo adicionar fundo e borda aos blocos de números. No array vipNumber do MikyouCountDownTimer, estão salvos [1 2 3 6 2 7]] o array vipnonNumer contém os caracteres de separação [horas minutos segundos] ou [ : : ]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<>(); 
} 
/** Sobrescrevendo o método setBackgroundSpan da superclasse 
* Sabemos que a configuração do estilo Span é principalmente controlar duas variáveis, índices start e end 
* para determinar o estilo da substring da posição start até end na string, representando o intervalo de posição de cada subcadeia de número na string inteira 
* mGapLen = 1,representa o comprimento de um bloco de intervalo, 
* por exemplo:12horas36minutos27A largura do intervalo entre "horas", "minutos" e "segundos" 
* Portanto, ao percorrer a coleção de Span, definimos Span para a string, 
* A análise mostra claramente que cada Span do bloco de número tem o índice de início: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); 
} 
//Neste momento, significa que a varredura de um bloco de valores foi concluída, portanto, é necessário atualizar a variável start com o valor deste bloco de valores 
start = start + vipNumbers[i].toCharArray().length; 
if (i < nonNumbers.length){ 
TimerUtils.setContentSpan(mSpan, mTextColorSpanList.get(i), start, start+mGapLen); 
start = start +mGapLen;//Se for uma separação, ainda é necessário adicionar a cada comprimento de separação e atualizar a variável start 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); 
mDateTv.setText(mSpan); 
} 
/** 
* Sobrescrever o método initSpanData da classe pai 
* Obter o objeto MikyouBackgroundSpan personalizado correspondente ao bloco de valores através do array number 
* Depois, através do objeto MikyouBackgroundSpan, definir o estilo de cada bloco de valores, incluindo fundo, borda, estilo de borda arredondada, e adicionar esses objetos à coleção 
* Obter o objeto ForegroundColorSpan de cada separação através do array nonNumber 
* Depois, através desses objetos, é possível definir o estilo de cada bloco de separação, porque só foi definido ForegroundColorSpan, então só pode ser definido 
* A cor do texto de cada bloco de separação, a maneira de setmGapSpanColor também fornece a personalização de estilo de cada separação externa 
* Na verdade, ainda é possível definir outros Span, a implementação também é很简单. 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
vipNumbers = TimerUtils.getNumInTimerStr(timeStr);//Obter cada número, não cada bloco de valores, e adicioná-lo ao array 
vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr);//Obter cada caractere de separação e adicioná-lo ao array 
for (int i = 0; i < vipNumbers.length; i++} 
for (int j = 0; j < vipNumbers[i].toCharArray().length; j++}//Porque é necessário obter cada número, ainda é necessário percorrer cada número em cada bloco de valores, então é necessário um loop de dois níveis 
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); 
} 
} 
}

Quarto, a classe TimerUtils é responsável por fornecer objetos de contagem regressiva de diferentes estilos para o cliente, portanto, esta classe estabelece diretamente uma relação com o cliente, realizando assim a ocultação das subclasses de contagem regressiva e a classe base para o exterior, refletindo a encapsulamento.

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; 
} 
//Obtém a parte numérica da string de contagem regressiva 
public static String[] getNumInTimerStr(String mTimerStr){ 
return mTimerStr.split("[^\\d]"); 
} 
//Obtém a stringa de não números dentro da string de contagem regressiva e filtra os números para recombinar em uma string, dividindo a string em um array de caracteres, ou seja, salvando os espaços entre a contagem regressiva 
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; 
} 
//Definir Span de Conteúdo 
public static void setContentSpan(SpannableString mSpan, Object span, int start, 
int end) { 
mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
} 
}

Agora vamos testar o uso de um TextView para implementar o contagem regressiva.

O uso deste contador regressivo é muito simples e conveniente, basta uma linha de código para implementar um estilo de contagem regressiva semelhante ao de Lojas como a JD.com e outras lojas de comércio eletrônico.

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); 
//默认样式倒计时每种样式下又对应四种时间的格式 
/** 
* 默认+formato de tempo1: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 de tempo2: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); 
/** 
* 默认+formato de tempo3: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); 
/** 
* 默认+formato de tempo4: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); 
//样式一倒计时,就是每块数值和每个间隔分开的样式,每种样式下又对应四种时间的格式 
/** 
* 样式一+formato de tempo1: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); 
/** 
* 样式一+formato de tempo2: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); 
/** 
* 样式一+formato de tempo3:JD_STYLE <-->TIME_STYLE_THREE = "dd dia HH hora mm minuto ss segundo" 
* */ 
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); 
/** 
* 样式一+formato de tempo4:JD_STYLE <-->TIME_STYLE_FOUR = "dd dia HH hora mm minuto" 
* */ 
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); 
/** 
* estilo dois+formato de tempo1: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); 
/** 
* estilo dois+formato de tempo2: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); 
/** 
* estilo dois+formato de tempo3:VIP_STYLE <-->TIME_STYLE_THREE = "dd dia HH hora mm minuto ss segundo" 
* */ 
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); 
/** 
* estilo dois+formato de tempo4:VIP_STYLE <-->TIME_STYLE_FOUR = "dd dia HH hora mm minuto" 
* */ 
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) { 
; 
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tv.getLayoutParams(); 
params.setMargins(20,20,20,20); 
tv.setLayoutParams(params); 
} 
}

dois arquivos drawable:

estilo com borda

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

estilo com fundo e borda

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

Agora vamos ver o que conseguimos.

Vamos ver os resultados da execução. De fato, seu estilo pode ser definido em muitos tipos, principalmente dependendo da sua criatividade e ideias. Se houver algo que falta na encapsulação do contagem regressiva, por favor, faça muitas sugestões. No entanto, ainda é bastante conveniente e simples de usar, uma linha de código pode resolver. Este contagem regressiva é usado em muitos lugares, você pode diretamente adicioná-lo ao seu próprio projeto se precisar.

Download Demo

O que foi mencionado acima é o que o editor apresentou a você sobre como usar TextView para implementar efeitos de contagem regressiva semelhantes ao Jingdong e Taobao no Android, esperando ajudar a todos. Se você tiver alguma dúvida, por favor, deixe um comentário e o editor responderá a você o mais rápido possível. Agradecemos muito o apoio do site de tutorial de gritos!

Declaração: O conteúdo deste artigo é extraído da Internet, pertence ao respectivo proprietário, é contribuído e carregado voluntariamente pelos usuários da Internet, o site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade legal relevante. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @) para denunciar, forneça provas relevantes e, se confirmado, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.

Você também pode gostar