Androidカスタム円カウントダウンの進捗バー


本論文の例では、Androidカウントダウンの進捗状況バーに展示されている具体的なコードを共有します。
効果のプレビュー

ソースコード転送ゲート:https://github.com/yanzhenjie/CircleTextProgressbar
実現と原理
この文字の円形のプログレスバーは多くのAPPで見たことがあります。例えば、APPのカウントダウン、ファイルのダウンロードカウントダウンなどです。
原理を分析してみると、このカスタムビューを見て慌てる学生がいるかもしれません。これはViewを継承するのですか?描くのですか?しかし、私たちも心配しないでください。この効果を実現するのは本当にsoeasuyです。以下は私と一緒にコア分析とコードを見てみましょう。
原理分析
まず上の図を観察します。いくつかの部分構成が必要です。
1.外で徐々に増加/減少する円形の進捗バー。
2.ラウンドプログレスバーの中間に表示されるテキスト。
3.丸印の進行形の外側に包まれた円。
4.ラウンドプログレスバーの中間の塗りつぶし色。
5.フォントの色/塗りつぶしの色クリックの色:ColorStateListクラス。
私たちは4つの部分が必要だと分析した。文字があると最初に思いついたのはもちろんTextViewです。字体の色の記録を少なくすることができます。中間の塗りつぶし色(プロトタイプはともかく)をクリックすると色が変わり、ColorStteList類が記録されます。残りのプログレスバー、輪郭円、塗りつぶし円は私達が描いたものです。
私がカプセル化したCirculeTextProgessbarの特徴
Circure TextProgressbarは自動カウントダウンに対応し、自動的に進捗を減少させ、自動的に進捗を増加させる。
自動で進行が必要なら、あなたが指定した属性を設定してから、start()を呼び出すと自動的にカウントダウンができます。歩き終わったらもう一度行って、自動進捗を呼び出してください。
自動的に進行したくないなら、set Progessを通じてシステムのプログレスのように進捗値を修正することができます。

//           ,0-100。
progressBar.setProgressType(CircleTextProgressbar.ProgressType.COUNT);
//      。
progressBar.setProgressLineWidth(30);//      。
//          ,  3000  。
progressBar.setTimeMillis(3500);
//        。
progressBar.setProgressColor(Color.RED);
//         。
progressBar.setOutLineColor(Color.RED);
//       。
progressBar.setInCircleColor(Color.RED);
//          ,       。
progressBar.start();
//          ,  100。
progressBar.setProgress(100);

ピットを踏む過程
実は久しぶりにカスタムViewを書いています。一部のものは本当に忘れてしまいました。だからこのViewを書く時、また前の穴を踏んでしまいました。他の友達が穴に入らないように、ここで私が踏んだ穴を記録してください。
ビューの描画エリア
ここで一つの問題があります。私たちが継承しているTextViewの文字は多くなったら長いです。そうすると、描いた円の幅は同じです。だから、TextViewで描いた円は一部か楕円形しか見えません。だから私たちはViewの描画エリアを拡大します。最初に思いついたのはラyout()の方法です。Viewの父のレイアウトがonLayout()の時にViewのラyout()を呼び出して、子供Viewのレイアウトを作るために、ラyoutの方法を書き直しました。

@Override
public void layout(int left, int top, int right, int bottom) {
 int w = right - left;
 int h = bottom - top;
 int size = w > h ? w : h;

 if (w > h) {
 bottom += (size - h);
 } else {
 right += (size - w);
 }
 super.layout(left, top, right, bottom);
}
このコードの原理は幅と高さで、その大きさはviewをこんなに最大のこの値に拡大します。
Viewを一つ置いたら、Layoutで効果が出てきて大丈夫ですが、Lineear Layoutに複数のViewを入れたら、いくつかのViewが重なっています。私ははっと悟ったのです。この尼瑪の家のLayoutはもう私の描いた区域の幅の高さを指定しました。他のViewのものを強引に占領しました。so、私はOnMeasre()を書き直すべきです。幅の高さを測る時、父に教えてあげます。どれぐらいの大きさがほしいですか?

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int width = getMeasuredWidth();
 int height = getMeasuredHeight();
 int size = width > height ? width : height;
 setMeasuredDimension(size, size);
}
このコードの意味は分かりやすいです。super.onMeasreを見て測定するときの幅の高さと大きさ、幅の高さを最大に設定します。父にLayoutの大きさを教えてください。描きたい時はどうやって遊びますか?
Viewを描画します
はい、肝心なところに来ました。前のものは全部できました。私たちの円の描き方を見ます。丸を描くにはonDrawの書き方を書き直します。
まずペンが必要です。
Paint mPaint=new Paint()
mPaint.set AntiAlias(true)//ぎざぎざを防ぐ
描画エリアに移動
私たちはgetDrawingRectによって描画領域を取得できます。描画領域からこの領域を計算すると円の半径が描画できます。

Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 getDrawingRect(bounds);//  view   

 int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height();
 float outerRadius = size / 2; //          
}
塗りつぶし円を描く
先ほどクリックした時に変色したと言っていましたので、ColorStateListを使ってここで初期化をします。xmlでこの属性を定義することをサポートします。

//       。
ColorStateList inCircleColors = ColorStateList.valueOf(Color.TRANSPARENT);

private void initialize(Context ctx, AttributeSet attributeSet) {
 TypedArray typedArray = ctx.obtainStyledAttributes(attributeSet, R.styleable.Progressbar);
 inCircleColors = typedArray.getColorStateList(R.styleable.Progressbar_circle_color);
 typedArray.recycle();
}
View xmlの属性をどうやって自分で決めたらいいのか分かりません。
クリック、Check、Selectの状態によって塗りつぶし円の色を描きます。塗りつぶしですので、ここでPaintのSteyleはFILLです。

int circleColor = inCircleColors.getColorForState(getDrawableState(), 0);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(circleColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth, mPaint);
円心は描画領域の円心であり、半径は領域円を描く半径から外郭円線の幅を引く。このようにちょうど円を塗りつぶして外郭円と重複しない。
外枠の円を描く
これは簡単です。中空の線なので、スタイルはSTROKEです。そして線の幅を設定して、ブラシの色を描きます。

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(outLineWidth);
mPaint.setColor(outLineColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth / 2, mPaint);
円心は描画領域の円心で、半径は円を描く半径で、外側の輪郭の円線の幅の半分を引いて、このようにちょうど外部の輪郭線と内部の塗りつぶし円がくっついています。
TextViewの字を描きます
私たちの絵とTextView自身の絵が重なっていないように、super.onDraw(canvas)を消しました。だからここでTextViewの字も書きます。
まずTextViewのデフォルトの絵筆を手に入れて、TextView自体のフォント色を設定して、鋸歯に抵抗して、美観のために強引に文字を中央に置く:

//  
Paint paint = getPaint();
paint.setColor(getCurrentTextColor());
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
float textY = bounds.centerY() - (paint.descent() + paint.ascent()) / 2;
canvas.drawText(getText().toString(), bounds.centerX(), textY, paint);
プログレスバーを描画します
プログレスバーは円ではないですよ。正確には円弧です。
デフォルトのブラシを使って、色、スタイルをSTROKEに設定して、線の幅を設定します。最後に指定された描画エリアと円心、角度:

RectF mArcRect = new RectF();
Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 getDrawingRect(bounds);//  view   
 ...

 //        。
 mPaint.setColor(progressLineColor);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(progressLineWidth);
 mPaint.setStrokeCap(Paint.Cap.ROUND);
 int deleteWidth = progressLineWidth + outLineWidth;
 //       
 mArcRect.set(bounds.left + deleteWidth / 2, bounds.top + deleteWidth / 2,
 bounds.right -deleteWidth / 2, bounds.bottom - deleteWidth / 2);
 canvas.drawArc(mArcRect, 0, 360 * progress / 100, false, mPaint);
}
ここの難点は指定された描画領域にあります。外部の輪郭線を覆うことができないので、外郭線の内部に近づけるためには、一番外側に円を描く領域が必要です。だから、(外周線の幅+プログレスバーの幅)/2を引く境界はプログレスバーの境界です。
完全なコードの描画と測定
ここに来てキーコードを全部消しました。自分で書いてみてください。ここで完全なonDrawとonMeasreのソースコードを貼り付けます。

private int outLineColor = Color.BLACK;
private int outLineWidth = 2;
private ColorStateList inCircleColors = ColorStateList.valueOf(Color.TRANSPARENT);
private int circleColor;
private int progressLineColor = Color.BLUE;
private int progressLineWidth = 8;
private Paint mPaint = new Paint();
private RectF mArcRect = new RectF();
private int progress = 100;
final Rect bounds = new Rect();

@Override
protected void onDraw(Canvas canvas) {
 //  view   
 getDrawingRect(bounds);

 int size = bounds.height() > bounds.width() ? bounds.width() : bounds.height();
 float outerRadius = size / 2;

 //     
 int circleColor = inCircleColors.getColorForState(getDrawableState(), 0);
 mPaint.setStyle(Paint.Style.FILL);
 mPaint.setColor(circleColor);
 canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth, mPaint);

 //    
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(outLineWidth);
 mPaint.setColor(outLineColor);
 canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius - outLineWidth / 2, mPaint);

 //  
 Paint paint = getPaint();
 paint.setColor(getCurrentTextColor());
 paint.setAntiAlias(true);
 paint.setTextAlign(Paint.Align.CENTER);
 float textY = bounds.centerY() - (paint.descent() + paint.ascent()) / 2;
 canvas.drawText(getText().toString(), bounds.centerX(), textY, paint);

 //    
 mPaint.setColor(progressLineColor);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(progressLineWidth);
 mPaint.setStrokeCap(Paint.Cap.ROUND);
 int deleteWidth = progressLineWidth + outLineWidth;
 mArcRect.set(bounds.left + deleteWidth / 2, bounds.top + deleteWidth / 2,
 bounds.right - deleteWidth / 2, bounds.bottom - deleteWidth / 2);

 canvas.drawArc(mArcRect, 0, 360 * progress / 100, false, mPaint);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int lineWidth = 4 * (outLineWidth + progressLineWidth);
 int width = getMeasuredWidth();
 int height = getMeasuredHeight();
 int size = (width > height ? width : height) + lineWidth;
 setMeasuredDimension(size, size);
}

現在既知の互換性問題の修復
 1.現在CirculeTextPrograesbarはReletiveLayotの中で高度が大きくなり、進捗状況が少し偏ります。修復方法は以下の通りです。
ReletiveLayotでCircure TextPrograesbarを使うなら、onMeasre()方法を書き換えないで、xmlでCircure TextProgresbarの幅の高さを指定してください。例えば50 dpに指定しても大丈夫です。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。