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

Implementación de efectos de cuenta regresiva de imitación de Jingdong y Taobao en TextView de Android

Hoy les traigo una actividad de temporizador inverso de un solo TextView que imita a varias APP de comercio electrónico como Jingdong, Taobao, VIPShop, etc. Recientemente, la compañía ha estado trabajando con horas extras y no he tenido tiempo para organizarlo, hoy es un día de descanso y quiero compartirlo con todos, solo para aprender juntos y para mi revisión posterior. ¿Por qué pensar en usar un TextView? Porque recientemente la compañía ha estado haciendo trabajo de optimización, uno de ellos es el estilo del temporizador inverso, el compañero que desarrolló el控件 utilizó múltiples TextView combinadas, lo que resultó en un código redundante bastante grande, por lo que el gerente de proyecto dijo: Xiao Hong, déjame optimizar esto y asegúrate de que tenga cierta capacidad de expansión, en ese momento estaba aturdido y no sabía por dónde comenzar la optimización. Luego revisé el temporizador inverso de varias APP como Jingdong, Ele.me, VIPShop, etc., y abrí la interfaz de nivel del desarrollador para mostrarlo, y descubrí que tienen un punto común: un View, sin usar múltiples TextView combinadas. Creo que todos saben que usar solo un TextView es mejor que usar múltiples TextView combinadas para implementar. A continuación, veamos algunos paneles para entenderlo.


Ver esto, todos los corazones naturalmente pensaron en implementar una View personalizada, sí, la View personalizada puede lograr tal efecto. Pero hoy no utilizamos la View personalizada, sino que utilizamos un TextView.

Debido a que el gerente de proyecto requiere que el código optimizado tenga una capacidad de expansión, por lo que el diseño de este código añade algunos conocimientos de orientación a objetos. Algunas ideas propias de diseño y arquitectura.

La idea de diseño de este demo:

          1. Escribir una clase base de temporizador inverso para implementar las funciones más comunes y básicas del temporizador inverso, sin estilo, que herede de la clase CountDownTimer y, en esta clase base

Guardar un objeto TextView y mostrar los datos del temporizador inverso en TextView cada vez, luego publicar un método getmDateTv() que devuelva un objeto TextView. Luego, simplemente obtener este objeto TextView y colocarlo en el diseño de la interfaz de visualización. Es muy conveniente.

          2. Then, for different styles of countdown, you just need to write different subclasses to inherit the most common countdown base class, then rewrite the two methods of setting data and setting style, and then you can add different styles to the most common countdown. Next time if you need to expand a new countdown style, you don't need to change the code of other classes, just write a derived class of a common countdown and rewrite the two methods, making the extensibility more flexible.

          3. Then, through a TimerUtils management class, take on the pressure of subclasses and superclasses, distribute the required functions of subclasses and superclasses 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, which are all allocated through this management class, avoiding the client to directly interact with the base class and subclasses of the countdown. This makes the encapsulation and concealment of the class evident.

Below is a 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.

 1Usage of CountDownTimer class.

    2. Usage of SpannableString.

    3. Encapsulation of MikyouCountDownTimer.

    4. Custom implementation of MikyouBackgroundSpan.

Firstly, through the above analysis, we first need to review the knowledge related to CountDownTimer. CountDownTimer is a very simple class, and we can look at 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(); 
}

Se puede ver que la duración total de la cuenta regresiva es millisFuture, y el countDownInterVal el paso de intervalo predeterminado es1000ms, por lo que todos los datos se inicializan a través de su constructor, luego es necesario sobrescribir un método de retroalimentación onTick, uno de los parámetros es el tiempo restante en milisegundos después de cada paso correspondiente. Luego, solo necesitamos poner en el método onTick cada1000ms para realizar la formateo de tiempo se puede obtener el formato de cuenta regresiva correspondiente, esto es lo que se ha implementado el estilo básico de cuenta regresiva. El formato de cuenta regresiva se utiliza el método formatDuration de la clase DurationFormatUtils del paquete common de apache lang, al pasar un formato de tiempo se convertirá automáticamente la cuenta regresiva al estilo de mTimePattern (HH:mm:ss o dd días HH horas mm minutos ss segundos).

Dos, repase las aplicaciones de SpannableString.

En Android, EditText se utiliza para editar texto, TextView para mostrar texto, pero a veces necesitamos establecer estilos y otros aspectos del texto. Android proporciona la clase SpannableString para procesar texto específico.

1) ForegroundColorSpan color del texto

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

2) BackgroundColorSpan color de fondo del texto

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

3) EstiloSpan estilo de fuente: negrita, cursiva, etc

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

4TextoSpan de tamaño relativo de fuente

private void setRelativeFontSpan() { 
SpannableString spanString = new SpannableString("tamaño relativo de fuente"); 
spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

5TextoSpan de fuente de texto

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

6TextoSpan de enlace de texto

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

7TextoSpan de imagen

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

8TextoSpan que puede hacer clic

private TextView textView; 
textView = (TextView)this.findViewById(R.id.textView); 
String text = "mostrar 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 que toda la longitud del texto es válida para desencadenar este evento 
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 
textView.setMovementMethod(LinkMovementMethod.getInstance());

9) UnderlineSpan subrayado

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

10) StrikethroughSpan

línea de eliminación

private void addStrikeSpan() { 
SpannableString spanString = new SpannableString("línea de eliminación"); 
StrikethroughSpan span = new StrikethroughSpan(); 
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

11) SuggestionSpan

Equivalente a un marcador de posición

12) MaskFilterSpan

Efectos de modificación, como desenfoque (BlurMaskFilter), relieve (EmbossMaskFilter)

13) RasterizerSpan

Efecto de rasterización

14) AbsoluteSizeSpan

Tamaño absoluto (tipografía de texto)

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

15) DynamicDrawableSpan configura la imagen, basada en la línea base del texto o alineación inferior.

16) TextAppearanceSpan

Apariencia de texto (que incluye tipo de letra, tamaño, estilo y color)

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

Bien, después de revisar los puntos de conocimiento anteriores, podemos comenzar a implementar realmente el demo, y luego todos juntos encapsulamos paso a paso el temporizador.

Primero, escribimos una clase base MikyouCountDownTimer, que hereda de la clase CountDownTimer y publica los métodos initSpanData y setBackgroundSpan para que los subclases de estilo de cuenta regresiva puedan usarlos. Esto puede implementar las funciones básicas de cuenta regresiva.

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; 
} 
//Establecer el estilo del Span del temporizador inverso,公布出给各个子类实现 
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; 
} 
}

La clase TimerUtils se utiliza para guardar diferentes formatos de cuenta regresiva, por ejemplo, HH:mm:ss, HH horas mm minutos ss segundos, dd días HH horas mm minutos ss segundos, etc. Ahora podemos ver un estilo básico simple.

Segundo, personalizar MikyouBackgroundSpan para heredar de ImageSpan, esta clase es muy importante y se utiliza para agregar estilos a TextView de cuenta regresiva, ¿por qué se puede usar un TextView para lograr esto?

No olvides que hay una clase muy potente llamada SpannableString, que permite establecer el estilo de cada carácter en una sección de cadena, muchos estilos. Finalmente, a través del método setSpan de TextView se puede pasar
Un objeto SpannableString completado. Pero ¿por qué necesito un Span personalizado? Porque es muy extraño que en android no haya muchos patrones de Span que permitan establecer directamente un archivo de objeto de drawable, así que he estado buscando en Internet y no he encontrado nada, hasta que en stackOverFlow encontré una solución proporcionada por un extranjero, que es sobrescribir ImageSpan y así se puede lograr la configuración de archivos de 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) { 
//Dibujar el fondo del contenido del texto 
paint.setTextSize(mTextSize); 
//Medir el ancho y la altura del texto, obtener a través de mTextBound 
paint.getTextBounds(text.toString(), start, end, mTextBound); 
//Establecer el ancho y la altura del fondo del texto, los parámetros pasados son left, top, right y bottom. 
maxWidth = maxWidth < mTextBound.width() ?63; mTextBound.width() : maxWidth; 
maxHeight = maxHeight < mTextBound.height() ?63; mTextBound.height() : maxHeight; 
//Establecer el ancho y la altura máximos es para evitar que el reloj en cuenta regresiva se repinta durante la transición de los números, lo que causará que el borde del reloj en cuenta regresiva tiemble en anchura y altura. 
// por lo que se obtiene la altura y anchura máximas cada vez en lugar de obtener la altura y anchura medidas cada vez} 
getDrawable().setBounds(0,0, maxWidth+mPaddingLeft+mPaddingRight,mPaddingTop+mPaddingBottom+maxHeight); 
//dibujar fondo de texto 
super.draw(canvas, text, start, end, x, top, y, bottom, paint); 
//establecer el color del texto 
paint.setColor(mTextColor); 
//establecer el tamaño de la fuente 
paint.setTextSize(mTextSize); 
int mGapX = (getDrawable().getBounds().width() - maxWidth)/2; 
int mGapY= (getDrawable().getBounds().height() - maxHeight)/2; 
//dibujar contenido de texto 
canvas.drawText(text.subSequence(start, end).toString(), x + mGapX , y - mGapY + maxHeight/3, paint); } 
}

Tercero, la implementación del temporizador en estilo de tres, el estilo de tres se refiere a por ejemplo:12hora36minuto27segundos o12:36:27es dividir los números y las horas, minutos, segundos o ":", luego personalizar cada bloque de número (12 36 27) y el estilo de separación (horas minutos segundos o :) incluyendo el fondo y el borde de los bloques de números. En el array number de MikyouCountDownTimer se guardan [12 36 27mientras que el array nonumer guarda los caracteres de separación [horas 分钟 segundos] o [ : :]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; 
} 
/** 
* Reescribir el método initSpanData de la clase padre 
* Obtener el objeto MikyouBackgroundSpan personalizado correspondiente a cada bloque de valores a través del array number 
* Luego, mediante el objeto MikyouBackgroundSpan, definir el estilo de cada bloque de valores, incluyendo fondo, borde, estilo de bordes redondeados, y luego agregar estos objetos a la colección 
* Obtener el objeto ForegroundColorSpan de cada separación a través del array nonNumber 
* Luego, mediante estos objetos, se puede definir el estilo de cada bloque de separación, ya que solo se definió ForegroundColorSpan, por lo que solo se puede definir 
* El color del texto de cada bloque de separación, el método setmGapSpanColor también permite personalizar libremente el estilo de cada separación 
* En realidad, se pueden definir otros Span, y la implementación también es muy simple 
* */ 
@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); 
} 
} 
/** Anula el método setBackgroundSpan de la clase padre 
* Sabemos que la configuración del estilo de Span principalmente controla dos variables: índices de inicio y final 
* para determinar el estilo de la subcadena de la posición de inicio a fin del texto 
* mGapLen = 1,que representa la longitud de un bloque de intervalo, 
* por ejemplo:12hora36minuto27La longitud del intervalo entre "segundo", "minuto", "segundo" 
* Por lo tanto, al recorrer la colección de Span, se establece Span en la cadena, 
* A través del análisis no es difícil darse cuenta de que el índice de inicio de Span de cada bloque de valor es: 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()){//Aquí es para evitar12:36:27Este estilo, este estilo tiene un intervalo de solo 2, por lo que se necesita hacer un juicio, evitar desbordamiento de array 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),end,end + mGapLen); 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//Este método es muy importante y debe llamarse, de lo contrario, el temporizador de cuenta regresiva dibujado será de estilo superpuesto 
mDateTv.setText(mSpan); 
} 
}

Cuatro, la implementación del temporizador de cuenta regresiva del estilo dos, el estilo dos es diferente al estilo uno en que por ejemplo:12hora36minuto27segundos o12:36:27es dividir cada número y las horas, minutos, segundos o ":", luego personalizar cada bloque de número (1 2 3 6 2 7) y estilo de separación (h f s o :) incluyendo agregar fondo y borde a los bloques de números. En el array vipNumber de MikyouCountDownTimer se guardan [1 2 3 6 2 7mientras que el array vipnonNumer guarda caracteres de separación [h f s] o [ : : ]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<>(); 
} 
/** Anula el método setBackgroundSpan de la clase padre 
* Sabemos que la configuración del estilo de Span principalmente controla dos variables: índices de inicio y final 
* para determinar el estilo de la subcadena de la posición de inicio a final del texto, que representa el rango de posición de cada subcadena numérica en la cadena completa 
* mGapLen = 1,que representa la longitud de un bloque de intervalo, 
* por ejemplo:12hora36minuto27La longitud del intervalo entre "segundo", "minuto", "segundo" 
* Por lo tanto, al recorrer la colección de Span, se establece Span en la cadena, 
* A través del análisis no es difícil darse cuenta de que el índice de inicio de Span de cada bloque de valor es: 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); 
} 
//En este momento, significa que se ha completado la recorrida de un bloque de valores, por lo que se necesita actualizar el valor de start para este bloque de valores 
start = start + vipNumbers[i].toCharArray().length; 
if (i < nonNumbers.length){ 
TimerUtils.setContentSpan(mSpan, mTextColorSpanList.get(i), start, start+mGapLen); 
start = start +mGapLen;//Si es una separación,还需加上每个间隔长度最后去更新start变量 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); 
mDateTv.setText(mSpan); 
} 
/** 
* Reescribir el método initSpanData de la clase padre 
* Obtener el objeto MikyouBackgroundSpan personalizado correspondiente a cada bloque de valores a través del array number 
* Luego, mediante el objeto MikyouBackgroundSpan, definir el estilo de cada bloque de valores, incluyendo fondo, borde, estilo de bordes redondeados, y luego agregar estos objetos a la colección 
* Obtener el objeto ForegroundColorSpan de cada separación a través del array nonNumber 
* Luego, mediante estos objetos, se puede definir el estilo de cada bloque de separación, ya que solo se definió ForegroundColorSpan, por lo que solo se puede definir 
* El color del texto de cada bloque de separación, el método setmGapSpanColor también permite personalizar libremente el estilo de cada separación 
* En realidad, se pueden definir otros Span, y la implementación también es muy simple 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
vipNumbers = TimerUtils.getNumInTimerStr(timeStr);//Obtener cada número, no cada bloque de valores, y agregarlo al array 
vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr);//Obtener cada carácter de separación y agregarlo al array 
for (int i = 0; i < vipNumbers.length; i++}{ 
for (int j = 0; j < vipNumbers[i].toCharArray().length; j++}{//Porque se necesita obtener cada número, se debe recorrer cada número en cada bloque de valores, por lo que se necesita un ciclo de dos niveles 
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); 
} 
} 
}

El cuarto, la clase de gestión TimerUtils, es principalmente proporcionar objetos de cuenta regresiva de diferentes estilos al cliente, por lo que esta clase se establece directamente en relación con el cliente, lo que permite que las subclases de cuenta regresiva y la clase base oculten la encapsulación hacia el exterior.

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; 
} 
//Obtener la parte de bloques numéricos en la cadena de cuenta regresivo 
public static String[] getNumInTimerStr(String mTimerStr){ 
return mTimerStr.split("[^\\d]"); 
} 
//Obtener la cadena de no números en la cadena de cuenta regresiva y filtrar los números para recombinarlos en una cadena, y dividir la cadena en un array de caracteres, es decir, guardar los intervalos entre el cuenta regresivo 
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; 
} 
//establecer el Span de contenido 
public static void setContentSpan(SpannableString mSpan, Object span, int start, 
int end) { 
mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
} 
}

Ahora vamos a probar el temporizador que implementamos utilizando un TextView.

El uso de este temporizador es muy simple y conveniente, solo se necesita una línea de código para implementar un estilo de temporizador similar a JD.com y otras aplicaciones de comercio electró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 tiempo1: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 tiempo2: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 tiempo3: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 tiempo4: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 tiempo1: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 tiempo2: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 tiempo3: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); 
/** 
* 样式一+formato de tiempo4: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); 
/** 
* estilo dos+formato de tiempo1: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 dos+formato de tiempo2: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 dos+formato de tiempo3: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); 
/** 
* estilo dos+formato de tiempo4: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); 
} 
}

dos archivos drawable:

estilo con borde

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

estilo con fondo y borde

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

Ahora veamos los resultados de nuestra ejecución.

Miren, los resultados de ejecución parecen estar bien, de hecho, el estilo puede definirse en muchos tipos, depende principalmente de la creatividad y la idea propia. Si esta encapsulación de cuenta regresiva tiene alguna deficiencia, por favor denuncien muchas sugerencias. Pero por ahora, es bastante conveniente y simple, una línea de código puede resolverlo. Este temporizador se utiliza en muchos lugares, si necesitan, pueden integrarlo directamente en sus proyectos.

Descarga de demo

Lo mencionado anteriormente es lo que el editor les ha presentado sobre cómo lograr efectos de cuenta regresiva similares a JD y Taobao en Android utilizando TextView, esperamos que les sea útil. Si tienen alguna pregunta, bienvenidos a dejar comentarios, el editor responderá a tiempo. Muchas gracias por el apoyo a la página web de tutorial de gritos!

Declaración: El contenido de este artículo se obtiene de la red, y los derechos de autor pertenecen al propietario original. El contenido se contribuye y sube por los usuarios de Internet, este sitio no posee los derechos de propiedad, no se ha editado artificialmente y no asume responsabilidades legales relacionadas. Si encuentra contenido sospechoso de copyright, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @) para denunciar, y proporcione evidencia relevante. Una vez confirmado, este sitio eliminará inmediatamente el contenido sospechoso de infracción.

Te gustará