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

AndroidでTextViewを使用して高仿京东淘宝の様々なカウントダウン効果を実現する

今日は、TextViewを使って高仿の京东、淘宝、唯品会などの電商APPのアクティビティカウントダウンを実装する方法を紹介します。最近は会社で残業が多く、整理する時間がありませんでしたが、今日は休暇を取って皆さんに共有したいと思いました。共同学習と、後で自分で復習するためにです。なぜTextViewを使うことを考えたのか?最近、会社で最適化の作業をしていますが、その中にはカウントダウンのスタイルがあります。元々開発したこのコントロールの同僚は複数のTextViewを結合して使用していましたが、コードの冗長性が高いとされました。そのため、プロジェクトマネージャーは「小宏、これをあなたに任せます。そして、ある程度の拡張性を確保する必要があります」と言いました。その時は困惑しました。どこから最適化を始めるか分かりませんでした。それで、京东、饿了么、唯品会などの各APPのカウントダウンを確認し、開発者でレイヤー表示を開き、共通の特徴が一つのViewで、複数のTextViewを結合して使用していないことに気づきました。誰もがTextViewを使って複数のTextViewを結合して使用するよりも良いと知っているでしょう。以下のいくつかの画面を見てみましょう。


これを見ると、自然とカスタムViewを実装すると思われるでしょう。確かに、カスタムViewはこのような効果を実現できます。しかし、今日はカスタムViewを使用しないで、TextViewを使用します。

プロジェクトマネージャーがこの最適化されたコードに拡張性を持たせることを要求したため、このコードの設計には面向オブジェクトの知識が加えられました。独自の設計やアーキテクチャの考えがあります。

このdemoの設計思想:

          1、カウントダウンの基本クラスを作成し、最も一般的で基本的なカウントダウンの機能を実現します。何もスタイルはなく、この基本クラスがCountDownTimerクラスを継承し、その基本クラスの中で

TextViewのオブジェクトを保存し、各回のカウントダウンのデータをTextViewに表示し、getmDateTv()メソッドを公開してTextViewオブジェクトを返すだけで、それで十分です。そのTextViewオブジェクトを表示するレイアウトに持っていくだけで非常に便利です。

          2、異なるスタイルのカウントダウンを実装するには、最も一般的なカウントダウン基底クラスを継承するサブクラスを書き、その中でデータの設定やスタイルの設定の二つのメソッドをオーバーライドします。これにより、最も一般的なカウントダウンに異なるスタイルを追加することができます。次に、新しいカウントダウンスタイルを拡張する必要がある場合、他のクラスのコードを変更する必要はなく、単に普通のカウントダウンの派生クラスを書き、二つのメソッドをオーバーライドすることで、拡張性がより柔軟になります。

          3、その後、TimerUtils管理クラスを通じて、サブクラスと親クラスの負担を集中管理し、サブクラスと親クラスが必要とする機能をTimerUtilsクラスに分担します。さらに、このTimerUtils管理クラスはクライアントと唯一のインタラクションを行うクラスであり、例えば、カウントダウンオブジェクトの取得やカウントダウンのTextViewオブジェクトの取得はこの管理クラスを通じて行われます。これにより、クライアントがカウントダウンの基底クラスやサブクラスと直接交わる必要がなくなり、クラスの封装性と隠匿性が示されます。

以下に、このDemoの設計されたシンプルなUMLクラス図を確認できます:


以上の考え方を分析した後、このDemoの実装に必要な知識点を見てみましょう。

 1、CountDownTimerクラスの使用法について見てみましょう。

    2、SpannableStringの使用法について説明します。

    3、MikyouCountDownTimerの包装を行います。

    4、MikyouBackgroundSpanの実装をカスタマイズします。

一、以上の分析を通じて、まずCountDownTimerに関する知識を振り返りましょう。CountDownTimerは非常にシンプルなクラスであり、そのソースコードを見ればその使用法が自然と分かります。

CountDownTimerは抽象クラスです。

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

見ての通り、カウントダウンの合計時間はmillisFutureで、countDownInterValの間隔ステップはデフォルトで1000ms、したがってデータは全て構造子で初期化され、onTickというコールバックメソッドをオーバーライドする必要があります。その中の引数は、適切なステップごとの残りの時間ミリ秒数です。それから、onTickメソッドで、1000ms時間ミリ秒数で時間フォーマットを行うと、対応する時間フォーマットのカウントダウンが得られます。これが最も基本的なカウントダウンスタイルです。カウントダウンフォーマットは、apacheのcommonのlangパッケージのDurationFormatUtilsクラスのformatDurationを使用して、時間フォーマットを入力すると、自動的にカウントダウンがmTimePatternのスタイル(HH:mm:ssまたはdd日HH時mm分ss秒)に変換されます。

二、复习一下有关SpannableString的用法。

在Android中,EditText用于编辑文本,TextView用于显示文本,但有时我们需要对其中的文本进行样式等方面的设置。Android为我们提供了SpannableString类来处理指定文本。

1)ForegroundColorSpan 文字颜色

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

2)BackgroundColorSpan 文字背景色

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

3)StyleSpan 文字样式:粗体、斜体等

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

4相対サイズ RelativeSizeSpan

private void setRelativeFontSpan() { 
SpannableString spanString = new SpannableString("フォントの相対サイズ"); 
spanString.setSpan(new RelativeSizeSpan(2.5f), 0, 6,Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

5テキストフォント TypefaceSpan

private void setTypefaceSpan() { 
SpannableString spanString = new SpannableString("テキストフォント"); 
spanString.setSpan(new TypefaceSpan("monospace"), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanText); 
}

6URLSpan テキストリンク

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

7イメージSpan 图片

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

8クリック可能なSpanテキストがあります

private TextView textView; 
textView = (TextView)this.findViewById(R.id.textView); 
String text = "显示Activity"; 
SpannableString spannableString = new SpannableString(text); 
spannableString.setSpan(new ClickableSpan() { 
@Override 
public void onClick(View widget) { 
Intent intent = new Intent(Main.this,OtherActivity.class); 
startActivity(intent); 
} 
// textの全体の長さがクリックイベントをトリガーする 
}, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 
textView.setMovementMethod(LinkMovementMethod.getInstance());

9アンダーラインスパン 下划线

private void addUnderLineSpan() { 
SpannableString spanString = new SpannableString("下划线"); 
アンダーラインスパン span = new UnderlineSpan(); 
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
tv.append(spanString); 
}

10ストライクスパン

删除线

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

11提案スパン

占位符に相当

12マスクフィルタースパン

ぼかし(BlurMaskFilter)、エンブス(EmbossMaskFilter)などの装飾効果

13レイスタライザースパン

レイトレーション効果

14) AbsoluteSizeSpan

絶対的な大きさ(テキストのフォント)

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

15) DynamicDrawableSpan 設定画像,テキストのベースラインまたは下部に基づいて並べます。

16) TextAppearanceSpan

文本外貌(包括字体、大小、样式和颜色)

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

好了,通过以上的复习知识点,现在我们就可以来真正开始demo的实现,然后我们一起来一步一步封装我们的倒计时。

一、编写一个MikyouCountDownTimer基类,让它去继承CountDownTimer类,并且公布出initSpanData和setBackgroundSpan方法用于其他样式倒计时的子类使用,它可以实现最基本倒计时的功能。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.os.CountDownTimer; 
import android.text.style.ForegroundColorSpan; 
import android.widget.TextView; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
import org.apache.commons.lang.time.DurationFormatUtils; 
import java.util.ArrayList; 
import java.util.List; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class MikyouCountDownTimer extends CountDownTimer{ 
private Context mContext;//传入されたコンテキストオブジェクト 
protected TextView mDateTv;//TextViewを使用して倒計時を実現します 
private long mGapTime;//設定された時間間隔を传入し、倒計時の合計時間として使用します 
private long mCount = 1000;//倒計時のステップサイズ 一般的には1000は每隔を表します1sをスキップします 
private String mTimePattern = "HH:mm:ss";//timePatternに传入された時間のスタイル 例:HH:mm:ss HH時mm分ss秒 dd日HH時mm分ss秒 
private String mTimeStr; 
protected List<MikyouBackgroundSpan> mBackSpanList; 
protected List<ForegroundColorSpan> mTextColorSpanList; 
private int mDrawableId; 
private boolean flag = false;//フラグflagを設定し、Spanのデータを一度に初期化するために使用されます 
protected String[] numbers;//この配列は、各倒計時文字を分割した日、時、分、秒の値を保存するために使用されます 
protected char[] nonNumbers;//日、時、分、秒の間隔を保存しました("日","時","分","秒"または":") 
//倒计时樣式の内マージン、フォントサイズ、フォント色、倒計時の間隔の色 
private int mSpanPaddingLeft,mSpanPaddingRight,mSpanPaddingTop,mSpanPaddingBottom; 
private int mSpanTextSize; 
private int mSpanTextColor; 
protected int mGapSpanColor; 
public MikyouCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
this(mContext,mGapTime,1000,mTimePattern,mDrawableId); 
} 
public MikyouCountDownTimer(Context mContext, long mGapTime, int mCount, String mTimePattern,int mDrawableId) { 
super(mGapTime,mCount); 
this.mContext = mContext; 
this.mGapTime = mGapTime;//カウントダウンの合計時間 
this.mCount = mCount;//各カウントダウンのステップサイズ、デフォルトは1000 
this.mDrawableId= mDrawableId;//背景のdrawableのidを設定する 
this.mTimePattern = mTimePattern;//時間のフォーマット:例えばHH:mm:ssまたはdd日HH時mm分ss秒など 
mBackSpanList = new ArrayList<>(); 
mTextColorSpanList = new ArrayList<>(); 
mDateTv = new TextView(mContext,null); 
} 
//これらのカウントダウンスタイル設定メソッドを公開し、外部から呼び出してカウントダウンのスタイルを柔軟にカスタマイズする 
public MikyouCountDownTimer setTimerTextSize(int textSize){ 
this.mSpanTextSize = textSize; 
return this; 
} 
public MikyouCountDownTimer setTimerPadding(int left,int top,int right,int bottom){ 
this.mSpanPaddingLeft = left; 
this.mSpanPaddingBottom = bottom; 
this.mSpanPaddingRight = right; 
this.mSpanPaddingTop = top; 
return this; 
} 
public MikyouCountDownTimer setTimerTextColor(int color){ 
this.mSpanTextColor = color; 
return this; 
} 
public MikyouCountDownTimer setTimerGapColor(int color){ 
this.mGapSpanColor = color; 
return this; 
} 
//カウントダウンのSpanのスタイルを設定し、各サブクラスに実装させる 
public void setBackgroundSpan(String timeStr) { 
if (!flag){ 
initSpanData(timeStr); 
flag = true; 
} 
mDateTv.setText(timeStr); 
} 
//設定倒計時的Span的數據,公布出給各個子類實現 
public void initSpanData(String timeStr) { 
numbers = TimerUtils.getNumInTimerStr(timeStr); 
nonNumbers = TimerUtils.getNonNumInTimerStr(timeStr); 
} 
protected void initBackSpanStyle(MikyouBackgroundSpan mBackSpan) { 
mBackSpan.setTimerPadding(mSpanPaddingLeft,mSpanPaddingTop,mSpanPaddingRight,mSpanPaddingBottom); 
mBackSpan.setTimerTextColor(mSpanTextColor); 
mBackSpan.setTimerTextSize(mSpanTextSize); 
} 
@Override 
public void onTick(long l) { 
if (l > 0) { 
mTimeStr = DurationFormatUtils.formatDuration(l, mTimePattern); 
//這是apache中的common的lang包中DurationFormatUtils類中的formatDuration,通過傳入 
//一個時間格式就會自動將倒計時轉換成相應的mTimePattern的樣式(HH:mm:ss或dd天HH時mm分ss秒) 
setBackgroundSpan(mTimeStr); 
} 
} 
@Override 
public void onFinish() { 
mDateTv.setText("倒計時結束"); 
} 
//用於返回顯示倒計時的TextView的對象 
public TextView getmDateTv() { 
startTimer(); 
return mDateTv; 
} 
public void cancelTimer(){ 
this.cancel(); 
} 
public void startTimer(){ 
this.start(); 
} 
public String getmTimeStr() { 
return mTimeStr; 
} 
}

TimerUtilsクラスは、異なる倒計時のフォーマットを保存するために使用されます。例えば、HH:mm:ss、HH時mm分ss秒、dd日HH時mm分ss秒などです。今や、基本的なスタイルを見てみましょう。

二、MikyouBackgroundSpanをImageSpanを継承するカスタムSpanを定義します。このクラスは非常に重要で、倒計時のTextViewにスタイルを追加するために使用されます。なぜTextViewを使うことができるのか?

SpannableStringクラスには、文字列の各文字のスタイルを設定できる非常に強力なクラスがあります。このクラスは、多くのスタイルを設定できます。最終的には、TextViewのsetSpanメソッドを通じて設定できます。
SpannableStringオブジェクトの設定が完了しました。しかし、なぜカスタムSpanが必要なのか?それは、Androidの多くのSpanスタイルのなかでも、drawableオブジェクトを直接設定できるものが一つもないという奇妙な理由からです。そのため、多くの検索を試みましたが、見つかりませんでした。最終的には、stackOverFlowで外国人が提供した解決策を見つけました。それは、ImageSpanをオーバーライドすることで、drawableファイルの設定が可能になります。

package com.mikyou.countdowntimer.myview; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.graphics.drawable.Drawable; 
import android.text.style.ImageSpan; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class MikyouBackgroundSpan extends ImageSpan { 
private Rect mTextBound; 
private int maxHeight = 0; 
private int maxWidth = 0; 
private int mPaddingLeft = 20; 
private int mPaddingRight = 20; 
private int mPaddingTop = 20; 
private int mPaddingBottom = 20; 
private int mTextColor = Color.GREEN; 
private int mTextSize = 50; 
public MikyouBackgroundSpan(Drawable d, int verticalAlignment) { 
super(d, verticalAlignment);} 
mTextBound = new Rect(); 
} 
public MikyouBackgroundSpan setTimerTextColor(int mTextColor) { 
this.mTextColor = mTextColor; 
return this; 
} 
public MikyouBackgroundSpan setTimerTextSize(int textSize){ 
this.mTextSize = textSize; 
return this; 
} 
public MikyouBackgroundSpan setTimerPadding(int left, int top, int right, int bottom){ 
this.mPaddingLeft = left; 
this.mPaddingRight = right; 
this.mPaddingBottom = bottom; 
this.mPaddingTop = top; 
return this; 
} 
@Override 
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 
//テキストの内容の背景を描画します 
paint.setTextSize(mTextSize); 
//テキストの幅と高さを測定し、mTextBoundから取得します 
paint.getTextBounds(text.toString(), start, end, mTextBound); 
//テキストの背景の幅と高さを設定するため、left, top, right, bottomの4つのパラメータを渡します 
maxWidth = maxWidth < mTextBound.width() ?63; mTextBound.width() : maxWidth; 
maxHeight = maxHeight < mTextBound.height() ?63; mTextBound.height() : maxHeight; 
//カウントダウンの数が切り替わる際に再描画を防ぐために、最大幅と最大高さを設定することで、カウントダウンの枠線の幅と高さが揺れ動くことがないようにする 
// したがって、最大の高さと幅を取得するのではなく、常に測定された高さと幅を取得します。 
getDrawable().setBounds(0,0, maxWidth+mPaddingLeft+mPaddingRight,mPaddingTop+mPaddingBottom+maxHeight); 
//テキスト背景を描画 
super.draw(canvas, text, start, end, x, top, y, bottom, paint); 
//テキストの色を設定 
paint.setColor(mTextColor); 
//フォントサイズを設定 
paint.setTextSize(mTextSize); 
int mGapX = (getDrawable().getBounds().width() - maxWidth)/2; 
int mGapY= (getDrawable().getBounds().height() - maxHeight)/2; 
//テキスト内容を描画 
canvas.drawText(text.subSequence(start, end).toString(), x + mGapX , y - mGapY + maxHeight/3, paint); } 
}

3. スタイル1のカウントダウン実装、スタイル1は例えば:12時36分27秒または12:36:27これは数値と時、分、秒または ":" で区切られ、各ブロックの数値をカスタマイズするために使用されます。12 36 27) そして間隔 (時 分 秒 または :) のスタイルを保存しています。これには、数値ブロックに背景と枠線を追加することも含まれます。MikyouCountDownTimer 内の number 配列には [12 36 27] に nonumer 配列は [時 分 秒 ] または [ : :]d の間隔文字を保存しています。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.text.SpannableString; 
import android.text.method.LinkMovementMethod; 
import android.text.style.ForegroundColorSpan; 
import android.text.style.ImageSpan; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class JDCountDownTimer extends MikyouCountDownTimer { 
private SpannableString mSpan; 
private Context mContext; 
private int mDrawableId; 
public JDCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
super(mContext, mGapTime, mTimePattern,mDrawableId); 
this.mContext = mContext; 
this.mDrawableId = mDrawableId; 
} 
/** 
* 親クラスのinitSpanDataメソッドをオーバーライドします。 
* number配列から各ブロックの数字に対応するカスタムMikyouBackgroundSpanオブジェクトを取得します。 
* それから、MikyouBackgroundSpanオブジェクトを使って各ブロックの数字のスタイルを定義します。これには背景、枠、枠の角のスタイルが含まれます。これらのオブジェクトを集合に追加します。 
* nonNumber配列から各間隔のForegroundColorSpanオブジェクトを取得します。 
* それから、これらのオブジェクトを使って各間隔ブロックのスタイルを定義できます。ForegroundColorSpanのみを定義したため、定義できるのは以下の通りです。 
* 各間隔ブロックのフォント色、setmGapSpanColorメソッドも外部から自由に各間隔のスタイルをカスタマイズするために提供されています。 
* 実際には他のSpanも定義できます。同様に、実現も非常に簡単です。 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
for (int i = 0; i<numbers.length;i++}); 
MikyouBackgroundSpan mBackSpan = new MikyouBackgroundSpan(mContext.getDrawable(mDrawableId), ImageSpan.ALIGN_BOTTOM); 
initBackSpanStyle(mBackSpan);}} 
mBackSpanList.add(mBackSpan); 
} 
for (int i= 0; i<nonNumbers.length;i++}); 
ForegroundColorSpan mGapSpan = new ForegroundColorSpan(mGapSpanColor); 
mTextColorSpanList.add(mGapSpan); 
} 
} 
/** 親クラスのsetBackgroundSpanメソッドをオーバーライドします 
* Spanのスタイルを設定する際には、start、endインデックスの2つの変数を制御する主な方法です 
* startからend位置の文字列のサブストリングのスタイルを設定するために使用されます。 
* mGapLen = 1,これは間隔ブロックの長さを示します、 
* 例えば、12時36分27秒の「時」、「分」、「秒」の間隔長さ 
* したがって、Spanセットを巡回して、文字列にSpanを設定します、 
* 通じて分析することができれば、各値ブロックのSpanのstartインデックスが明らかになります:start = i*numbers[i].length() + i*mGapLen; 
* end = start + numbers[i].length(); 
* */ 
@Override 
public void setBackgroundSpan(String timeStr) { 
super.setBackgroundSpan(timeStr); 
int mGapLen = 1; 
mSpan = new SpannableString(timeStr); 
for (int i = 0;i<mBackSpanList.size();i++}); 
int start = i*numbers[i].length() + i*mGapLen; 
int end = start + numbers[i].length(); 
TimerUtils.setContentSpan(mSpan,mBackSpanList.get(i),start,end); 
if (i < mTextColorSpanList.size()){//ここでは、12:36:27このスタイルでは、間隔が2つしかないため、配列越界を防ぐために判断を行う必要があります。 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),end,end + mGapLen); 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance());//このメソッドは非常に重要であり、呼び出さないと描画されたカウントダウンは重なり合ったスタイルになります。 
mDateTv.setText(mSpan); 
} 
}

4、スタイル2のカウントダウンの実装、スタイル2はスタイル1とは異なり、例えば:12時36分27秒または12:36:27これは、各数値と時、分、秒または":"で区切られ、各ブロックの数値をカスタマイズするためです。1 2 3 6 2 7)と間隔(時 分 秒 または :)のスタイルが含まれており、数値ブロックに背景と枠を追加することもできます。MikyouCountDownTimerのvipNumber配列には[1 2 3 6 2 7]]vipnonNumer配列には[時 分 秒 ]または[ : :]dの間隔文字が保存されています。

package com.mikyou.countdowntimer.bean; 
import android.content.Context; 
import android.text.SpannableString; 
import android.text.method.LinkMovementMethod; 
import android.text.style.ForegroundColorSpan; 
import android.text.style.ImageSpan; 
import com.mikyou.countdowntimer.myview.MikyouBackgroundSpan; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
import java.util.ArrayList; 
import java.util.List; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class VIPCountDownTimer extends MikyouCountDownTimer { 
private SpannableString mSpan; 
private Context mContext; 
private int mDrawableId; 
private List<MikyouBackgroundSpan> mSpanList; 
private String[] vipNumbers; 
private char[] vipNonNumbers; 
public VIPCountDownTimer(Context mContext, long mGapTime, String mTimePattern,int mDrawableId) { 
super(mContext, mGapTime, mTimePattern,mDrawableId); 
this.mContext = mContext; 
this.mDrawableId = mDrawableId; 
mSpanList = new ArrayList<>(); 
} 
/** 親クラスのsetBackgroundSpanメソッドをオーバーライドします 
* Spanのスタイルを設定する際には、start、endインデックスの2つの変数を制御する主な方法です 
* startからend位置の文字列のサブストリングのスタイルを設定し、各数字サブストリングが全体の文字列の中での位置範囲を示します 
* mGapLen = 1,これは間隔ブロックの長さを示します、 
* 例えば、12時36分27秒の「時」、「分」、「秒」の間隔長さ 
* したがって、Spanセットを巡回して、文字列にSpanを設定します、 
* 通じて分析することができれば、各値ブロックのSpanのstartインデックスが明らかになります:start = i*numbers[i].length() + i*mGapLen; 
* end = start + numbers[i].length(); 
* */ 
@Override 
public void setBackgroundSpan(String timeStr) { 
int mGapLen = 1; 
mSpan = new SpannableString(timeStr); 
initSpanData(timeStr); 
int start = 0 ; 
int count =0; 
for (int i=0;i<vipNumbers.length;i++}); 
for (int j=start;j<start + vipNumbers[i].toCharArray().length;j++,count++}); 
TimerUtils.setContentSpan(mSpan,mSpanList.get(count),j,j+mGapLen); 
} 
//これは、あるブロックの数字の巡回が完了したことを示し、そのブロックの数字をstart変数に更新する必要があります。 
start = start + vipNumbers[i].toCharArray().length; 
if (i < nonNumbers.length){ 
TimerUtils.setContentSpan(mSpan,mTextColorSpanList.get(i),start,start+mGapLen); 
start = start +mGapLen;//間隔であれば、各間隔の長さを加え、start変数を更新する必要があります。 
} 
} 
mDateTv.setMovementMethod(LinkMovementMethod.getInstance()); 
mDateTv.setText(mSpan); 
} 
/** 
* 親クラスのinitSpanDataメソッドをオーバーライドします。 
* number配列から各ブロックの数字に対応するカスタムMikyouBackgroundSpanオブジェクトを取得します。 
* それから、MikyouBackgroundSpanオブジェクトを使って各ブロックの数字のスタイルを定義します。これには背景、枠、枠の角のスタイルが含まれます。これらのオブジェクトを集合に追加します。 
* nonNumber配列から各間隔のForegroundColorSpanオブジェクトを取得します。 
* それから、これらのオブジェクトを使って各間隔ブロックのスタイルを定義できます。ForegroundColorSpanのみを定義したため、定義できるのは以下の通りです。 
* 各間隔ブロックのフォント色、setmGapSpanColorメソッドも外部から自由に各間隔のスタイルをカスタマイズするために提供されています。 
* 実際には他のSpanも定義できます。同様に、実現も非常に簡単です。 
* */ 
@Override 
public void initSpanData(String timeStr) { 
super.initSpanData(timeStr); 
vipNumbers = TimerUtils.getNumInTimerStr(timeStr);//各数字を取得しますが、各ブロックの数字ではありません、配列に追加します。 
vipNonNumbers = TimerUtils.getNonNumInTimerStr(timeStr);//各間隔文字を取得し、配列に追加します。 
for (int i=0;i<vipNumbers.length;i++}); 
for (int j=0;j<vipNumbers[i].toCharArray().length;j++});//各数字を取得する必要があるため、各ブロックの数字の各数字を再び巡回する必要がありますので、二重ループが必要です。 
MikyouBackgroundSpan mSpan = new MikyouBackgroundSpan(mContext.getDrawable(mDrawableId), ImageSpan.ALIGN_BOTTOM); 
initBackSpanStyle(mSpan); 
mSpanList.add(mSpan); 
} 
} 
for (int i= 0; i<vipNonNumbers.length;i++}); 
ForegroundColorSpan mGapSpan = new ForegroundColorSpan(mGapSpanColor); 
mTextColorSpanList.add(mGapSpan); 
} 
} 
}

四、TimerUtils管理クラスは、クライアントに異なるスタイルのカウントダウンのオブジェクトを提供することを主目的としており、このクラスは直接クライアントと関係を築き、カウントダウンサブクラスとベースクラスが外界に対して隠蔽されることを表現して、プログラミングの隠蔽性を示しています。

package com.mikyou.countdowntimer.utils; 
import android.content.Context; 
import android.graphics.Color; 
import android.text.SpannableString; 
import android.text.Spanned; 
import android.text.style.ForegroundColorSpan; 
import com.mikyou.countdowntimer.bean.JDCountDownTimer; 
import com.mikyou.countdowntimer.bean.MikyouCountDownTimer; 
import com.mikyou.countdowntimer.bean.VIPCountDownTimer; 
/** 
* Created by mikyou on 16-10-22. 
*/ 
public class TimerUtils { 
public static final int JD_STYLE = 0; 
public static final int VIP_STYLE = 1; 
public static final int DEFAULT_STYLE = 3; 
public static final String TIME_STYLE_ONE = "HH:mm:ss"; 
public static final String TIME_STYLE_TWO = "HH時mm分ss秒"; 
public static final String TIME_STYLE_THREE = "dd天HH時mm分ss秒"; 
public static final String TIME_STYLE_FOUR = "dd天HH時mm分"; 
public static MikyouCountDownTimer getTimer(int style,Context mContext, long mGapTime, String mTimePattern, int mDrawableId){ 
MikyouCountDownTimer mCountDownTimer = null; 
switch (style){ 
case JD_STYLE: 
mCountDownTimer = new JDCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
case VIP_STYLE: 
mCountDownTimer = new VIPCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
case DEFAULT_STYLE: 
mCountDownTimer = new MikyouCountDownTimer(mContext,mGapTime,mTimePattern,mDrawableId); 
break; 
} 
return mCountDownTimer; 
} 
//倒计时文字列の数値ブロック部分を取得します。 
public static String[] getNumInTimerStr(String mTimerStr){ 
return mTimerStr.split("[^\\d]"); 
} 
//倒计时中の文字列から非数値の文字列を取得し、数値をフィルタリングして再構成し、文字列を文字配列に分割し、倒计时の間隔を保存します。 
public static char[] getNonNumInTimerStr(String mTimerStr){ 
return mTimerStr.replaceAll("\\d","").toCharArray();} 
} 
//设置字体颜色 
public static ForegroundColorSpan getTextColorSpan(String color){ 
ForegroundColorSpan mSpan = null; 
if (mSpan == null){ 
mSpan = new ForegroundColorSpan(Color.parseColor(color)); 
} 
return mSpan; 
} 
//内容のSpanを設定します 
public static void setContentSpan(SpannableString mSpan, Object span, int start, 
int end) { 
mSpan.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
} 
}

今から、TextViewを使用して作成したカウントダウンをテストしましょう。

このカウントダウンを使用するのは非常に簡単で便利で、一行のコードで高仿の京东やその他のECサイトのアプリのカウントダウンスタイルを実現できます。

package com.mikyou.countdowntimer; 
import android.graphics.Color; 
import android.os.Bundle; 
import android.support.v7.app.AppCompatActivity; 
import android.view.Gravity; 
import android.widget.LinearLayout; 
import android.widget.TextView; 
import com.mikyou.countdowntimer.utils.TimerUtils; 
public class MainActivity extends AppCompatActivity { 
private LinearLayout parent; 
private int padding =10; 
private int textSize = 40; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
parent = (LinearLayout) findViewById(R.id.parent); 
//默认样式倒计时每种样式下又对应四种时间的格式 
/** 
* 默认+時間フォーマット1:DEFAULT_STYLE <--> TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,0) 
.getmDateTv(); 
parent.addView(tv); 
setmLayoutParams(tv); 
/** 
* 默认+時間フォーマット2:DEFAULT_STYLE <--> TIME_STYLE_TWO = "HH时mm分ss秒" 
* */ 
TextView tv1 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,0) 
.getmDateTv(); 
parent.addView(tv1); 
setmLayoutParams(tv1); 
/** 
* 默认+時間フォーマット3:DEFAULT_STYLE <--> TIME_STYLE_THREE = "dd天HH时mm分ss秒" 
* */ 
TextView tv2 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,0) 
.getmDateTv(); 
parent.addView(tv2); 
setmLayoutParams(tv2); 
/** 
* 默认+時間フォーマット4:DEFAULT_STYLE <--> TIME_STYLE_FOUR = "dd天HH时mm分" 
* */ 
TextView tv3 = TimerUtils.getTimer(TimerUtils.DEFAULT_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,0) 
.getmDateTv(); 
parent.addView(tv3); 
setmLayoutParams(tv3); 
//样式一倒计时,就是每块数值和每个间隔分开的样式,每种样式下又对应四种时间的格式 
/** 
* 样式一+時間フォーマット1:JD_STYLE <--> TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv4= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,R.drawable.timer_shape) 
.setTimerPadding(10,10,10,10)//设置内间距 
.setTimerTextColor(Color.BLACK)//设置字体颜色 
.setTimerTextSize(40)//设置字体大小 
.setTimerGapColor(Color.BLACK)//设置间隔的颜色 
.getmDateTv();//拿到TextView对象 
parent.addView(tv4); 
setmLayoutParams(tv4); 
/** 
* 样式一+時間フォーマット2:JD_STYLE <--> TIME_STYLE_TWO = "HH时mm分ss秒" 
* */ 
TextView tv5= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,R.drawable.timer_shape2) 
.setTimerPadding(10,10,10,10) 
.setTimerTextColor(Color.WHITE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv5); 
setmLayoutParams(tv5); 
/** 
* 样式一+時間フォーマット3:JD_STYLE <-->TIME_STYLE_THREE = "dd日HH時mm分ss秒" 
* */ 
TextView tv6= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,R.drawable.timer_shape2) 
.setTimerPadding(10,10,10,10) 
.setTimerTextColor(Color.YELLOW) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv6); 
setmLayoutParams(tv6); 
/** 
* 样式一+時間フォーマット4:JD_STYLE <-->TIME_STYLE_FOUR = "dd日HH時mm分" 
* */ 
TextView tv7= TimerUtils.getTimer(TimerUtils.JD_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLUE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv7); 
setmLayoutParams(tv7); 
/** 
* スタイル二+時間フォーマット1:VIP_STYLE <-->TIME_STYLE_ONE = "HH:mm:ss" 
* */ 
TextView tv8= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_ONE,R.drawable.timer_shape) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLACK) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv8); 
setmLayoutParams(tv8); 
/** 
* スタイル二+時間フォーマット2:VIP_STYLE <-->TIME_STYLE_TWO = "HH时mm分ss秒" 
* */ 
TextView tv9= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_TWO,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.WHITE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv9); 
setmLayoutParams(tv9); 
/** 
* スタイル二+時間フォーマット3:VIP_STYLE <-->TIME_STYLE_THREE = "dd日HH時mm分ss秒" 
* */ 
TextView tv10= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_THREE,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.YELLOW) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv10); 
setmLayoutParams(tv10); 
/** 
* スタイル二+時間フォーマット4:VIP_STYLE <-->TIME_STYLE_FOUR = "dd日HH時mm分" 
* */ 
TextView tv11= TimerUtils.getTimer(TimerUtils.VIP_STYLE,this,120000000,TimerUtils.TIME_STYLE_FOUR,R.drawable.timer_shape2) 
.setTimerPadding(15,15,15,15) 
.setTimerTextColor(Color.BLUE) 
.setTimerTextSize(40) 
.setTimerGapColor(Color.BLACK) 
.getmDateTv(); 
parent.addView(tv11); 
setmLayoutParams(tv11); 
} 
private void setmLayoutParams(TextView tv) { 
tv.setGravity(Gravity.CENTER_HORIZONTAL); 
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tv.getLayoutParams(); 
params.setMargins(20,20,20,20); 
tv.setLayoutParams(params); 
} 
}

二つのdrawableファイル:

帯枠線スタイル

<?xml version="1.0" encoding="utf-8"-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>

帯背景と枠線スタイル

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

今すぐ、私たちが実行した結果を見てみましょう。

実行結果は申し分なく、スタイルは定義できる種類がたくさんあります。主に自分の創造性とアイデア次第です。この倒计时のラッパーにはまだ不十分な点があれば、多くのご意見をいただければ幸いです。しかし、現在の使用は非常に便利で簡単です。一行のコードで解決できます。この倒计时は多くの場所で使用されています。必要であれば、自分のプロジェクトに直接導入できます。

デモダウンロード

上記は、編集者が皆さんに紹介したAndroidでTextViewを使用して高仿京东淘宝各种倒计时効果についてです。皆さんに役立つことを願っています。何かご不明な点があれば、コメントを残してください。編集者は皆さんに迅速に返信します。皆さんのサポートに感謝しています。

声明:この記事の内容はインターネットから収集され、著作権者に帰属します。インターネットユーザーが自発的に貢献し、自己でアップロードしました。このサイトは所有権を持ちません。また、人間による編集は行われていません。著作権に関する問題がある場合は、メールを送信してください:notice#oldtoolbag.com(メールを送信する際には、#を@に変更してください。報告を行い、関連する証拠を提供してください。一旦確認ができたら、このサイトは即座に侵害が疑われるコンテンツを削除します。)

基礎教程
おすすめ