一款不錯的Android環形進度條
阿新 • • 發佈:2019-01-02
現在市面上有很多app都使用了環形進度條,可以反映當前使用者的一個進度狀態,今天給大家帶來一款環形的,帶動畫和數字提示的進度條,拋磚引玉,希望大家喜歡。話不多說,先上簡單的效果圖:
可以設定動畫,它的進度條和數字提示會隨著動畫進行而變化。以下是具體程式碼:
順帶需要加進arr.xml的自定義屬性:import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.support.annotation.Keep; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import your project.R; /** * Circular progress view */ public class CircularProgressView extends View { /** * Factor to convert the factor to paint the arc. * <p/> * In this way the developer can use a more user-friendly [0..1f] progress */ public static final int PROGRESS_FACTOR = -360; /** * Property Progress of the outer circle. * <p/> * The progress of the circle. If is set * to FALSE, this property will be used to indicate the completion of the outer circle [0..1f]. * <p/> * If set to TRUE, the drawable will activate the loading mode, where the drawable will * show a 90º arc which will be spinning around the outer circle as much as progress goes. */ public static final String PROGRESS_PROPERTY = "progress"; /** * Rectangle where the filling ring will be drawn into. */ protected final RectF arcElements; /** * Width of the filling ring. */ protected int ringWidth; /** * Paint object to draw the element. */ protected final Paint paint; /** * Ring progress. */ protected float progress; /** * Color for the completed ring. */ protected int ringColor; /** * Ring progress title. */ protected String progressTitle; /** * default gradient color for the progress ring. */ private LinearGradient shader; private Rect rec; public CircularProgressView(Context context) { this(context, null); } public CircularProgressView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CircularProgressView); ringColor = array.getColor(R.styleable.CircularProgressView_ringColor, 0); ringWidth = (int) array.getDimension(R.styleable.CircularProgressView_ringWidth, 20); progressTitle = array.getString(R.styleable.CircularProgressView_progressTitle); array.recycle(); this.progress = 0; this.paint = new Paint(); this.paint.setAntiAlias(true); this.arcElements = new RectF(); rec = new Rect(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // Calculations on the different components sizes int size = Math.min(canvas.getHeight(), canvas.getWidth()); float outerRadius = (size / 2) - (ringWidth / 2); float offsetX = (canvas.getWidth() - outerRadius * 2) / 2; float offsetY = (canvas.getHeight() - outerRadius * 2) / 2; int halfRingWidth = ringWidth / 2; float arcX0 = offsetX + halfRingWidth; float arcY0 = offsetY + halfRingWidth; float arcX = offsetX + outerRadius * 2 - halfRingWidth; float arcY = offsetY + outerRadius * 2 - halfRingWidth; paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(ringWidth); paint.setStrokeCap(Paint.Cap.ROUND); arcElements.set(arcX0, arcY0, arcX, arcY); paint.setColor(Color.GRAY); canvas.drawArc(arcElements, 0, 360, false, paint); if (ringColor != 0) { paint.setColor(ringColor); canvas.drawArc(arcElements, -90, -progress, false, paint); } else { if (shader == null) { shader = new LinearGradient(0, offsetY, 0, offsetY + outerRadius * 2, new int[]{Color.parseColor("#B4ED50"), Color.parseColor("#429321")}, null, Shader.TileMode.CLAMP); } paint.setShader(shader); canvas.drawArc(arcElements, -90, -progress, false, paint); } int progressText = -(int) (progress / 3.6); String v = progressText + "%"; paint.setShader(null); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.WHITE); paint.setTextSize(spToPx(30)); paint.getTextBounds(v, 0, v.length(), rec); int textwidth = rec.width(); int textheight = rec.height(); // draw the center words if (progressTitle != null && !progressTitle.isEmpty()) { canvas.drawText(v, (canvas.getWidth() - textwidth) / 2, (canvas.getHeight() + textheight) / 2 - dpToPx(20), paint); paint.setTextSize(spToPx(16)); paint.getTextBounds(progressTitle, 0, progressTitle.length(), rec); int textwidth1 = rec.width(); int textheight1 = rec.height(); canvas.drawText(progressTitle, (canvas.getWidth() - textwidth1) / 2, (canvas.getHeight() + textheight1) / 2 + dpToPx(20), paint); } else { canvas.drawText(v, (canvas.getWidth() - textwidth) / 2, (canvas.getHeight() + textheight) / 2, paint); } } /** * Change sp to px. * * @param sp the sp value. * @return the px value. */ private float spToPx(int sp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getContext().getResources().getDisplayMetrics()); } /** * Change dp to px. * * @param dp the dp value. * @return the px value. */ private float dpToPx(int dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } /** * Returns the progress of the outer ring. * <p/> * Will output a correct value only when the indeterminate mode is set to FALSE. * * @return Progress of the outer ring. */ public float getProgress() { return progress / PROGRESS_FACTOR; } /** * Sets the progress [0..1f]. * * @param progress Sets the progress. */ @Keep public void setProgress(float progress) { this.progress = PROGRESS_FACTOR * progress; invalidate(); } /** * Gets the filled ring color. * * @return Returns the filled ring color. */ public int getRingColor() { return ringColor; } /** * Sets the progress ring color. * * @param ringColor Ring color in #AARRGGBB format. */ public void setRingColor(int ringColor) { this.ringColor = ringColor; invalidate(); } /** * Sets the ring width. * * @param ringWidth Default ring width. */ public void setRingWidth(int ringWidth) { this.ringWidth = ringWidth; invalidate(); } /** * Gets the ring width. * * @return Returns the ring width. */ public int getRingWidth() { return ringWidth; } /** * Sets the progress title. * * @param progressTitle Sets the progress. */ public void setProgressTitle(String progressTitle) { this.progressTitle = progressTitle; invalidate(); } /** * Start progress animation or show the progress directly. * * @param progress the progress you set. * @param isAnim weather to show the animation. */ public void startAnim(float progress, boolean isAnim) { AnimatorSet animation = new AnimatorSet(); ObjectAnimator progressAnimation = ObjectAnimator.ofFloat(this, CircularProgressView.PROGRESS_PROPERTY, 0f, progress); progressAnimation.setDuration(isAnim ? 1000 : 0); progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); //another kind of animation // ObjectAnimator colorAnimator = ObjectAnimator.ofInt(drawable, CircularProgressDrawable.RING_COLOR_PROPERTY, // getResources().getColor(android.R.color.holo_red_dark), // getResources().getColor(android.R.color.holo_green_light)); // colorAnimator.setEvaluator(new ArgbEvaluator()); // colorAnimator.setDuration(3600); // animation.playTogether(progressAnimation, colorAnimator); animation.play(progressAnimation); animation.start(); } }
<declare-styleable name="CircularProgressView"> <attr name="ringWidth" format="dimension" /> <attr name="ringColor" format="color|reference" /> <attr name="progressTitle" format="string" /> </declare-styleable>咦,怎麼註釋全是英文?當然是為了提(bu)升(yao)逼(jie)格(yi)啦。由上可看出,可以在xml檔案中直接定義的屬性有ringWidth,即為圓環寬度;ringColor,為圓環顏色;progressTitle即為數字提示下面的文字提示,其餘屬性也可根據需求進行擴充套件。寬度可定義為march_parent,然後定義一個確切的高度,如效果圖即為180dp。用法很簡單,只需要在xml中定義上面的元件就可以了。當然,要讓它展示進度和動畫效果還必須呼叫startAnim(float progress, boolean isAnim)這個方法,傳入(0-100)的進度,第二個引數控制是否有動畫,不想展示動畫傳false就可以了。其他的我也不多說啦,請大家去程式碼裡面體會吧。