English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Heute bringe ich Ihnen einen Countdown, der nur mit einem TextView eine Countdown-Funktion wie bei verschiedenen E-Commerce-Apps wie JD.com, Taobao und Vip.com nachahmt. In letzter Zeit habe ich wegen Überstunden keine Zeit gehabt, dies zu organisieren. Heute, da ich endlich eine Pause habe, möchte ich dies mit Ihnen teilen, nur um gemeinsam zu lernen und mich selbst zu复习en. Warum sollte man einen TextView verwenden? Weil unser Unternehmen in letzter Zeit Optimierungsarbeiten durchführt, darunter auch eine Countdown-Stil. Der Kollege, der den ursprünglichen Widget entwickelt hat, hat mehrere TextViews zusammengefügt, was zu einem großen Code-Overhead führt. Daher sagte der Projektmanager: Xiao Hong, das überlasse ich dir zur Optimierung, und es muss eine gewisse Erweiterbarkeit gewährleistet sein. Damals war ich verwirrt und wusste nicht, wo ich anfangen sollte. Dann habe ich die Countdowns der verschiedenen Apps wie JD.com, Ele.me und Vip.com überprüft und die Hierarchieansicht in den Entwicklern geöffnet, und ich habe festgestellt, dass sie alle eine gemeinsame Eigenschaft haben: Ein View, das keine mehrere TextViews zusammengefügt verwendet. Ich glaube, dass jeder weiß, dass ein TextView, der alleine verwendet wird, besser ist als das Zusammenfügen mehrerer TextViews. Schauen wir uns einfach einige Oberflächen an, um dies zu verstehen.
Wenn man das sieht, denkt man natürlich sofort an die Implementierung durch ein benutzerdefiniertes View. Richtig, ein benutzerdefiniertes View kann solche Effekte realisieren. Aber heute verwenden wir nicht ein benutzerdefiniertes View, sondern einen TextView.
Da der Projektmanager verlangt, dass der optimierte Code erweiterbar ist, habe ich bei der Designgestaltung einige objektorientierte Kenntnisse hinzugefügt. Es gibt einige eigene Design- und Architekturideen.
Die Designidee dieses Demos:
1. Schreiben Sie eine Basisklasse für den Countdown, um die grundlegenden und häufigsten Countdown-Funktionen zu implementieren. Diese Klasse hat keinen Stil und erbt von der Klasse CountDownTimer. Und in dieser Basisklasse
Speichern Sie ein TextView-Objekt und zeigen Sie die Daten des Countdowns bei jedem Durchlauf im TextView an. Anschließend können Sie eine Methode getmDateTv()公布, die ein TextView-Objekt zurückgibt. Dann können Sie das TextView-Objekt einfach in die Layout-Struktur der Benutzeroberfläche anzeigen. Das ist sehr bequem.
2. Und für verschiedene Arten von Countdowns genügt es, verschiedene Unterklassen zu schreiben, die die einfachste Countdown-Basisklasse erweitern, und dann die beiden Methoden zur Einstellung der Daten und der Einstellung des Stils zu überschreiben. Dies ermöglicht es, dem einfachsten Countdown verschiedene Stile hinzuzufügen. Wenn man später neue Countdown-Styles erweitern muss, muss der Code anderer Klassen nicht geändert werden. Es genügt, eine neue Unterklasse der einfachen Countdown zu schreiben und die beiden Methoden zu überschreiben, um die Flexibilität der Erweiterbarkeit zu erhöhen.
3. Dann übernimmt eine TimerUtils-Verwaltungsklasse die zentrale Last zwischen den Unterklassen und den Oberklassen, um sicherzustellen, dass die Funktionen, die von den Unterklassen und Oberklassen benötigt werden, auf die TimerUtils-Klasse verteilt werden. Und diese TimerUtils-Verwaltungsklasse ist die einzige Klasse, die mit dem Client kommuniziert, z.B. die Zuweisung des Countdown-Objekts und die Zuweisung des TextView-Objekts für den Countdown, die durch diese Verwaltungsklasse bereitgestellt werden. Dies verhindert, dass der Client direkt mit der Basisklasse und den Unterklassen des Countdowns interagiert. Dies zeigt die Encapsulation und Verborgenheit der Klasse.
Hier können Sie die einfache UML-Klassendiagramm dieses Demos betrachten:
Durch die obige Analyse betrachten wir nun, welche Kenntnisse für die Implementierung dieses Demos erforderlich sind.
1Verwendung der Klasse CountDownTimer.
2. Verwendung von SpannableString.
3. Verpackung von MikyouCountDownTimer.
4. Implementierung von MikyouBackgroundSpan.
Durch die obige Analyse müssen wir zunächst einmal das Wissen über CountDownTimer auffrischen. CountDownTimer ist eine sehr einfache Klasse, und wir können durch ihre Quelldatei sehen, wie sie verwendet wird. Die Verwendung ist natürlich bekannt.
CountDownTimer ist eine abstrakte Klasse.
// // Quellcode neu erstellt aus einer .class Datei durch IntelliJ IDEA // (betrieben von Fernflower Dekompiler) // 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(); }
Man kann sehen, dass die Gesamtzeit des Countdowns millisFuture ist und der countDownInterVal Intervallschritt ist standardmäßig1000ms, daher werden alle Daten durch den Konstruktor initialisiert und dann müssen wir eine Callback-Methode überschreiben, die onTick heißt, und ein Parameter ist die verbleibende Zeit in Millisekunden nach jedem entsprechenden Schritt. Dann müssen wir in der onTick-Methode die verbleibende Zeit nach jedem Schritt übergeben,1000ms, um die Zeit in das entsprechende Zeitformat zu formatieren, erhalten wir so den umgekehrten Countdown, das ist der einfachste Countdown-Stil, der implementiert wurde. Das Formatieren des Countdowns erfolgt mit dem Format des apache-commons-lang-Pakets, der Klasse DurationFormatUtils, durch Übergeben eines Zeitformats wird der Countdown automatisch in das Muster von mTimePattern umgewandelt (HH:mm:ss oder dd Tage HH Stunden mm Minuten ss Sekunden).
Zwei, erinnern wir uns an die Verwendung von SpannableString.
In Android wird EditText zum Bearbeiten von Text verwendet, TextView zum Anzeigen von Text, aber manchmal müssen wir den Text in Bezug auf den Stil und andere Einstellungen konfigurieren. Android bietet uns die Klasse SpannableString an, um spezifische Texte zu bearbeiten.
1) ForegroundColorSpan Textfarbe
private void setForegroundColorSpan() {
SpannableString spanString = new SpannableString("Vordergrundfarbe");
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.append(spanString);
}
2) BackgroundColorSpan Texthintergrundfarbe
private void setBackgroundColorSpan() {
SpannableString spanString = new SpannableString("Hintergrundfarbe");
BackgroundColorSpan span = new BackgroundColorSpan(Color.YELLOW);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.append(spanString);
}
3) StyleSpan Schriftstil: Fett, Kursiv usw.
private void setStyleSpan() { SpannableString spanString = new SpannableString("Fett Kursiv"); StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC); spanString.setSpan(span, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); tv.append(spanString); }
4RelativeSizeSpan Relative Size
private void setRelativeFontSpan() { SpannableString spanString = new SpannableString("Font Relative Size"); spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); tv.append(spanString); }
5TypefaceSpan Text Schriftart
private void setTypefaceSpan() { SpannableString spanString = new SpannableString("Text Schriftart"); spanString.setSpan(new TypefaceSpan("monospace"), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); tv.append(spanText); }
6URLSpan Text Hyperlink
private void addUrlSpan() { SpannableString spanString = new SpannableString("Hyperlink"); URLSpan span = new URLSpan("http:");//www.baidu.com); spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); tv.append(spanString); }
7ImageSpan Bild
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 Text hat einen Klickereignis
private TextView textView; textView = (TextView)this.findViewById(R.id.textView); String text = "Activity anzeigen"; 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); } // Stellt dar, dass die gesamte Länge des Textes zum Auslösen dieses Ereignisses gültig ist }, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(spannableString); textView.setMovementMethod(LinkMovementMethod.getInstance());
9) UnderlineSpan Unterstrichen
private void addUnderLineSpan() { SpannableString spanString = new SpannableString("Unterstrichen"); UnderlineSpan span = new UnderlineSpan(); spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); tv.append(spanString); }
10) StrikethroughSpan
Durchgestrichen
private void addStrikeSpan() { SpannableString spanString = new SpannableString("Durchgestrichen"); StrikethroughSpan span = new StrikethroughSpan(); spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); tv.append(spanString); }
11) SuggestionSpan
Äquivalent zu einem Platzhalter
12) MaskFilterSpan
Verzierungseffekte wie Verwischen (BlurMaskFilter), Reliefschatten (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); }
Nun, nach der Wiederholung der vorherigen Lernpunkte können wir mit der eigentlichen Implementierung des Demos beginnen und gemeinsam Schritt für Schritt den Countdown verpacken.
Erstellen Sie eine Basisklasse MikyouCountDownTimer, die die Klasse CountDownTimer erbt und die Methoden initSpanData und setBackgroundSpan öffnet, damit sie von Unterklassen für andere Stile der Countdown-Animation verwendet werden können. Sie kann die grundlegenden Funktionen eines Countdowns realisieren.
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;//Der übergebene Kontextobjekt protected TextView mDateTv;//Ein TextView, der den Countdown implementiert private long mGapTime;//Die übergebene Zeitintervallsetzung ist die Gesamtzeit des Countdowns private long mCount = 1000;//Schrittweite des Countdowns,一般在1000 bedeutet jede1s springt einmal private String mTimePattern = "HH:mm:ss";//timePattern das übergebene Zeitmuster wie: HH:mm:ss HH Std mm Min ss Sek dd Tag HH Std mm Min ss Sek private String mTimeStr; protected List<MikyouBackgroundSpan> mBackSpanList; protected List<ForegroundColorSpan> mTextColorSpanList; private int mDrawableId; private boolean flag = false;//Setzt das Markenzeichen flag, um zu steuern, dass die Daten des Spans einmal initialisiert werden protected String[] numbers;//Dieses Array wird verwendet, um die numerischen Werte von Tagen, Stunden, Minuten und Sekunden zu speichern, die nach dem Zerlegen jedes Countdown-Zeichens erhalten werden protected char[] nonNumbers;//Speichert den Abstand zwischen Tagen, Stunden, Minuten und Sekunden ("Tag", "Stunde", "Minute", "Sekunde" oder ":") //Wird für den Innenabstand des Countdown-Stils, die Zeichengröße, die Zeichencolor und die Farbe des Countdown-Intervalls verwendet 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;//The total duration of the countdown this.mCount = mCount;//The step length of each countdown, the default is1000 this.mDrawableId= mDrawableId;//Used to set the id of the drawable for the background this.mTimePattern = mTimePattern;//Time format: such as HH:mm:ss or dd days HH hours mm minutes ss seconds, etc. mBackSpanList = new ArrayList<>(); mTextColorSpanList = new ArrayList<>(); mDateTv = new TextView(mContext,null); } //Announce these methods to set the countdown style, for external calls, thus allowing flexible customization of the countdown style 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; } //Set the style of the countdown Span, announced for implementation by subclasses 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; } }
Die TimerUtils-Klasse wird verwendet, um verschiedene Countdown-Formate zu speichern, z.B. HH:mm:ss, HH Stunden mm Minuten ss Sekunden, dd Tage HH Stunden mm Minuten ss Sekunden usw. Nun können wir die einfachen Grundstile betrachten.
Zwei: Definieren Sie MikyouBackgroundSpan, um ImageSpan zu erben, diese Klasse ist sehr wichtig und wird verwendet, um dem TextView des Countdowns Stile hinzuzufügen, warum kann ein TextView verwendet werden, um dies zu erreichen
Vergessen Sie nicht, dass es eine sehr mächtige Klasse gibt, nämlich SpannableString, die es ermöglicht, den Stil jedes Zeichens in einem Abschnitt der Zeichenfolge zu setzen, viele Stile. Schließlich kann dies durch den Methodenaufruf setSpan in TextView erreicht werden
Ein SpannableString-Objekt ist eingerichtet. Aber warum ist ein eigener Span erforderlich? Weil es in Android so viele Span-Styles gibt, die keinen drawable-Objektdatei direkt einstellen können, also habe ich im Internet viel gesucht und nichts gefunden, bis ich auf stackOverFlow fand, dass ein Ausländer eine Lösung vorgeschlagen hat, indem er den ImageSpan überschreibt, so dass es möglich ist, drawable-Dateien einzurichten
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) { //Der Hintergrund des Textinhalts wird gezeichnet paint.setTextSize(mTextSize); //Die Breite und Höhe des Textes werden gemessen, und durch mTextBound erhalten paint.getTextBounds(text.toString(), start, end, mTextBound); //Die Breite und Höhe des Texthintergrunds werden eingestellt, und die übergebenen Parameter sind left, top, right und bottom. maxWidth = maxWidth < mTextBound.width() ?63; mTextBound.width() : maxWidth; maxHeight = maxHeight < mTextBound.height() ?63; mTextBound.height() : maxHeight; //Die Festlegung der maximalen Breite und Höhe dient dazu, zu verhindern, dass während des Wechsels der Ziffern im Countdown eine Neuerzeichnung stattfindet, was zu Schwingungen der Breite und Höhe des Countdown-Rahmens führen könnte. // 因此每次取得最大的高度和宽度而不是每次都去取测量的高度和宽度 getDrawable().setBounds(0, 0, maxWidth+mPaddingLeft+mPaddingRight, mPaddingTop+mPaddingBottom+maxHeight); //绘制文本背景 super.draw(canvas, text, start, end, x, top, y, bottom, paint); //设置文本的颜色 paint.setColor(mTextColor); //设置字体的大小 paint.setTextSize(mTextSize); int mGapX = (getDrawable().getBounds().width() - maxWidth)/2; int mGapY = (getDrawable().getBounds().height() - maxHeight)/2; //绘制文本内容 canvas.drawText(text.subSequence(start, end).toString(), x + mGapX, y - mGapY + maxHeight/3, paint); } }
三、样式一的倒计时实现,样式一指的是例如:12Stunde36Minute27Sekunde oder12:36:27就是将数值和时、分、秒或者":"分隔开,然后去自定义每块数值(12 36 27)和间隔(时 分 秒 或 :)的样式,包括给数值块加背景和边框。在MikyouCountDownTimer中的number数组中保存着[12 36 27而在nonumer数组中保存着[时 分 秒]或[ : :]d的间隔字符。
package com.mikyou.countdowntimer.bean; import android.content.Context; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; import com.mikyou.countdowntimer.utils.TimerUtils; /** * Created by mikyou on 16-10-22. */ public class JDCountDownTimer extends MikyouCountDownTimer { private SpannableString mSpan; private Context mContext; private int mDrawableId; public JDCountDownTimer(Context mContext, long mGapTime, String mTimePattern, int mDrawableId) { super(mContext, mGapTime, mTimePattern, mDrawableId); this.mContext = mContext; this.mDrawableId = mDrawableId; } /** * Rewrite the parent class's initSpanData method * Get the custom MikyouBackgroundSpan object corresponding to each block of numbers through the number array * Then, through MikyouBackgroundSpan object, define the style of each block of numbers including background, border, border rounded style, and then add these objects to the collection * Get each space's ForegroundColorSpan object through the nonNumber array * Then, through these objects, the style of each space block can be defined, because only ForegroundColorSpan is defined, so only * Font color of each space block, setmGapSpanColor is also provided for external free customization of each space style * In fact, other spans can also be defined, and their implementation is also very 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); } } /** Die Methode setBackgroundSpan der übergeordneten Klasse überschreiben * Wir wissen, dass die Stilsetzung von Span hauptsächlich die zwei Variablen start, end Indizes kontrolliert * um den Stil der Teilzeichenfolge der Zeichenfolge von start bis end zu bestimmen * mGapLen = 1, was die Länge eines Intervallsblöcks darstellt, * Zum Beispiel:12Stunde36Minute27Die "Stunde", "Minute", "Sekunde" der Sekundenintervalle * Daher kann durch das Durchlaufen der Span-Sammlung, der String Span gesetzt werden, * Durch die Analyse lässt sich leicht erkennen, dass der Startindex des Spans jedes numerischen Blocks: 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()){//Hier ist es darum,12:36:27Dieser Stil hat nur 2 Abstände, daher muss eine Überprüfung durchgeführt werden, um Array-Überschreitungen zu verhindern TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),end,end + mGapLen); } } mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//Diese Methode ist sehr wichtig und muss aufgerufen werden,否则绘制的倒计时就是重叠的样式 mDateTv.setText(mSpan); } }
Vier, die Implementierung des Countdowns im Stil 2, der sich vom Stil 1 unterscheidet, zum Beispiel:12Stunde36Minute27Sekunde oder12:36:27d.h. jede Zahl und Stunde, Minute, Sekunde oder ":" zu trennen, dann die numerischen Blöcke selbst zu stylen(1 2 3 6 2 7) und das Trennzeichen (Stunde, Minute, Sekunde oder :) für das Stil, einschließlich dem Hinzufügen von Hintergrund und Rahmen zu den numerischen Blöcken. Das Array vipNumber im MikyouCountDownTimer speichert [1 2 3 6 2 7]] und das Array vipnonNumer speichert die Trennzeichen [Stunde, Minute, Sekunde] oder [ : : ]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<>(); } /** Die Methode setBackgroundSpan der übergeordneten Klasse überschreiben * Wir wissen, dass die Stilsetzung von Span hauptsächlich die zwei Variablen start, end Indizes kontrolliert * um die Stil des Unterstrings von start bis end zu bestimmen, der den Positionsbereich jeder numerischen Unterzeichenkette im gesamten String darstellt * mGapLen = 1, was die Länge eines Intervallsblöcks darstellt, * Zum Beispiel:12Stunde36Minute27Die "Stunde", "Minute", "Sekunde" der Sekundenintervalle * Daher kann durch das Durchlaufen der Span-Sammlung, der String Span gesetzt werden, * Durch die Analyse lässt sich leicht erkennen, dass der Startindex des Spans jedes numerischen Blocks: 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); } //At this point, it means that the traversal of a block of numbers is complete, so the start variable of this block of numbers needs to be updated start = start + vipNumbers[i].toCharArray().length; if (i < nonNumbers.length){ TimerUtils.setContentSpan(mSpan, mTextColorSpanList.get(i), start, start+mGapLen); start = start +mGapLen;//If it is a space, you still need to add each space length and update the start variable } } mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); mDateTv.setText(mSpan); } /** * Rewrite the parent class's initSpanData method * Get the custom MikyouBackgroundSpan object corresponding to each block of numbers through the number array * Then, through MikyouBackgroundSpan object, define the style of each block of numbers including background, border, border rounded style, and then add these objects to the collection * Get each space's ForegroundColorSpan object through the nonNumber array * Then, through these objects, the style of each space block can be defined, because only ForegroundColorSpan is defined, so only * Font color of each space block, setmGapSpanColor is also provided for external free customization of each space style * In fact, other spans can also be defined, and their implementation is also very simple. * */ @Override public void initSpanData(String timeStr) { super.initSpanData(timeStr); vipNumbers = TimerUtils.getNumInTimerStr(timeStr);//Get each number, not each block of numbers, and add it to the array vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr);//Get each space character and add it to the array for (int i = 0; i < vipNumbers.length; i++){ for (int j = 0; j < vipNumbers[i].toCharArray().length; j++){//Because we need to get each number, we still need to traverse each number in each block of numbers, so we need a two-level loop 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); } } }
Vierter Abschnitt: TimerUtils-Verwaltungsklasse, die hauptsächlich verschiedene Stile von Countdown-Objekten für den Client bereitstellt. Daher wird diese Klasse direkt mit dem Client verknüpft, um die Kapselung der Countdown-Unterklassen und Basisklassen gegenüber der Außenwelt zu verbergen.
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; } //Erhalten Sie die numerischen Blöcke im Countdown-String public static String[] getNumInTimerStr(String mTimerStr){ return mTimerStr.split("[^\\d]"); } //Erhalten Sie die nicht-numerischen Zeichenfolgen im Countdown-String und filtern Sie die numerischen Zeichenfolgen heraus, um einen neuen String neu zu kombinieren. Und teilen Sie den String in ein Zeichenarray auf, das die Intervalle im Countdown speichert 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; } //den Span des Inhalts einstellen public static void setContentSpan(SpannableString mSpan, Object span, int start, int end) { mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } }
Lassen Sie uns nun testen, wie wir einen Countdown mit einem TextView implementieren.
Die Verwendung dieses Countdowners ist sehr einfach und bequem. Es ist nur eine Zeile Code erforderlich, um ein Countdown-Stil zu implementieren, der dem von JD.com und anderen E-Commerce-Apps sehr ähnlich ist.
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); //默认样式倒计时每种样式下又对应四种时间的格式 /** * 默认+Zeitformat1: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); /** * 默认+Zeitformat2: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); /** * 默认+Zeitformat3: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); /** * 默认+Zeitformat4: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); //样式一倒计时,就是每块数值和每个间隔分开的样式,每种样式下又对应四种时间的格式 /** * 样式一+Zeitformat1: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); /** * 样式一+Zeitformat2: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); /** * 样式一+Zeitformat3:JD_STYLE <-->TIME_STYLE_THREE = "dd Tag 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); /** * 样式一+Zeitformat4:JD_STYLE <-->TIME_STYLE_FOUR = "dd Tag 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); /** * Stil zwei+Zeitformat1: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); /** * Stil zwei+Zeitformat2: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); /** * Stil zwei+Zeitformat3:VIP_STYLE <-->TIME_STYLE_THREE = "dd Tag 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); /** * Stil zwei+Zeitformat4:VIP_STYLE <-->TIME_STYLE_FOUR = "dd Tag 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) { ; LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tv.getLayoutParams(); params.setMargins(20,20,20,20); tv.setLayoutParams(params); } }
zwei drawable-Dateien:
mit Rahmengestaltung
<?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>
mit Hintergrund- und Rahmenstil
<?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>
Sehen wir uns jetzt das Ergebnis unseres Laufs an.
Sehen Sie sich das Ergebnis an, es ist wirklich gut, und der Stil kann viele Arten definieren, hauptsächlich abhängig von Ihrer Kreativität und Ihren Ideen. Wenn es bei diesem Countdown-Paket noch Verbesserungen geben kann, freuen wir uns über Ihre Vorschläge. Aber es ist immer noch sehr bequem und einfach zu verwenden, eine Zeile Code genügt. Dieser Countdown wird in vielen Bereichen verwendet, und wenn Sie ihn benötigen, können Sie ihn direkt in Ihr eigenes Projekt einbinden.
Wie oben erwähnt, habe ich Ihnen von der Implementierung von Countdown-Effekten in Android mit TextView vorgestellt, die ähnlich wie bei JD.com und Taobao sind. Ich hoffe, das hilft Ihnen. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht, ich werde rechtzeitig antworten. Ich danke Ihnen auch sehr für Ihre Unterstützung für die Website "Ruf zu den Lehrern"!
Erklärung: Der Inhalt dieses Artikels wurde aus dem Internet übernommen und gehört dem Urheberrechtsinhaber. Der Inhalt wurde von Internetnutzern freiwillig und selbstständig hochgeladen. Diese Website besitzt keine Eigentumsrechte und hat den Inhalt nicht manuell bearbeitet. Sie übernimmt auch keine rechtlichen Verantwortlichkeiten. Wenn Sie verdächtige Urheberrechtsinhalte finden, sind Sie herzlich eingeladen, eine E-Mail an notice#w zu senden.3codebox.com (Bitte ersetzen Sie # durch @, wenn Sie eine E-Mail senden. Melden Sie uns bitte, und fügen Sie relevante Beweise bei. Sobald die Angelegenheit überprüft wurde, wird die Website den verdächtigen Inhalt sofort löschen.)