Android編程基于自定義View實現(xiàn)絢麗的圓形進(jìn)度條功能示例
本文實例講述了Android編程基于自定義View實現(xiàn)絢麗的圓形進(jìn)度條功能。分享給大家供大家參考,具體如下:
本文包含兩個組件,首先上效果圖:
1.ProgressBarView1(支持拖動):
2.ProgressBarView2(不同進(jìn)度值顯示不同顏色,不支持拖拽):
代碼不多,注釋也比較詳細(xì),全部貼上了:
(一)ProgressBarView1:
/** * 自定義絢麗的ProgressBar. */ public class ProgressBarView1 extends View { /** * 進(jìn)度條所占用的角度 */ private static final int ARC_FULL_DEGREE = 300; /** * 弧線的寬度 */ private int STROKE_WIDTH; /** * 組件的寬,高 */ private int width, height; /** * 進(jìn)度條最大值和當(dāng)前進(jìn)度值 */ private float max, progress; /** * 是否允許拖動進(jìn)度條 */ private boolean draggingEnabled = false; /** * 繪制弧線的矩形區(qū)域 */ private RectF circleRectF; /** * 繪制弧線的畫筆 */ private Paint progressPaint; /** * 繪制文字的畫筆 */ private Paint textPaint; /** * 繪制當(dāng)前進(jìn)度值的畫筆 */ private Paint thumbPaint; /** * 圓弧的半徑 */ private int circleRadius; /** * 圓弧圓心位置 */ private int centerX, centerY; public ProgressBarView1(Context context) { super(context); init(); } public ProgressBarView1(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ProgressBarView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { progressPaint = new Paint(); progressPaint.setAntiAlias(true); textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setAntiAlias(true); thumbPaint = new Paint(); thumbPaint.setAntiAlias(true); //使用自定義字體 textPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fangz.ttf")); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (width == 0 || height == 0) { width = getWidth(); height = getHeight(); //計算圓弧半徑和圓心點 circleRadius = Math.min(width, height) / 2; STROKE_WIDTH = circleRadius / 12; circleRadius -= STROKE_WIDTH; centerX = width / 2; centerY = height / 2; //圓弧所在矩形區(qū)域 circleRectF = new RectF(); circleRectF.left = centerX - circleRadius; circleRectF.top = centerY - circleRadius; circleRectF.right = centerX + circleRadius; circleRectF.bottom = centerY + circleRadius; } } private Rect textBounds = new Rect(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float start = 90 + ((360 - ARC_FULL_DEGREE) >> 1); //進(jìn)度條起始點 float sweep1 = ARC_FULL_DEGREE * (progress / max); //進(jìn)度劃過的角度 float sweep2 = ARC_FULL_DEGREE - sweep1; //剩余的角度 //繪制起始位置小圓形 progressPaint.setColor(Color.WHITE); progressPaint.setStrokeWidth(0); progressPaint.setStyle(Paint.Style.FILL); float radians = (float) (((360.0f - ARC_FULL_DEGREE) / 2) / 180 * Math.PI); float startX = centerX - circleRadius * (float) Math.sin(radians); float startY = centerY + circleRadius * (float) Math.cos(radians); canvas.drawCircle(startX, startY, STROKE_WIDTH / 2, progressPaint); //繪制進(jìn)度條 progressPaint.setStrokeWidth(STROKE_WIDTH); progressPaint.setStyle(Paint.Style.STROKE);//設(shè)置空心 canvas.drawArc(circleRectF, start, sweep1, false, progressPaint); //繪制進(jìn)度條背景 progressPaint.setColor(Color.parseColor("#d64444")); canvas.drawArc(circleRectF, start + sweep1, sweep2, false, progressPaint); //繪制結(jié)束位置小圓形 progressPaint.setStrokeWidth(0); progressPaint.setStyle(Paint.Style.FILL); float endX = centerX + circleRadius * (float) Math.sin(radians); float endY = centerY + circleRadius * (float) Math.cos(radians); canvas.drawCircle(endX, endY, STROKE_WIDTH / 2, progressPaint); //上一行文字 textPaint.setTextSize(circleRadius >> 1); String text = (int) (100 * progress / max) + ""; float textLen = textPaint.measureText(text); //計算文字高度 textPaint.getTextBounds("8", 0, 1, textBounds); float h1 = textBounds.height(); //% 前面的數(shù)字水平居中,適當(dāng)調(diào)整 float extra = text.startsWith("1") ? -textPaint.measureText("1") / 2 : 0; canvas.drawText(text, centerX - textLen / 2 + extra, centerY - 30 + h1 / 2, textPaint); //百分號 textPaint.setTextSize(circleRadius >> 2); canvas.drawText("%", centerX + textLen / 2 + extra + 5, centerY - 30 + h1 / 2, textPaint); //下一行文字 textPaint.setTextSize(circleRadius / 5); text = "可用內(nèi)存充足"; textLen = textPaint.measureText(text); textPaint.getTextBounds(text, 0, text.length(), textBounds); float h2 = textBounds.height(); canvas.drawText(text, centerX - textLen / 2, centerY + h1 / 2 + h2, textPaint); //繪制進(jìn)度位置,也可以直接替換成一張圖片 float progressRadians = (float) (((360.0f - ARC_FULL_DEGREE) / 2 + sweep1) / 180 * Math.PI); float thumbX = centerX - circleRadius * (float) Math.sin(progressRadians); float thumbY = centerY + circleRadius * (float) Math.cos(progressRadians); thumbPaint.setColor(Color.parseColor("#33d64444")); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 2.0f, thumbPaint); thumbPaint.setColor(Color.parseColor("#99d64444")); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 1.4f, thumbPaint); thumbPaint.setColor(Color.WHITE); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 0.8f, thumbPaint); } private boolean isDragging = false; @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!draggingEnabled) { return super.onTouchEvent(event); } //處理拖動事件 float currentX = event.getX(); float currentY = event.getY(); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //判斷是否在進(jìn)度條thumb位置 if (checkOnArc(currentX, currentY)) { float newProgress = calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max; setProgressSync(newProgress); isDragging = true; } break; case MotionEvent.ACTION_MOVE: if (isDragging) { //判斷拖動時是否移出去了 if (checkOnArc(currentX, currentY)) { setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max); } else { isDragging = false; } } break; case MotionEvent.ACTION_UP: isDragging = false; break; } return true; } private float calDistance(float x1, float y1, float x2, float y2) { return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } /** * 判斷該點是否在弧線上(附近) */ private boolean checkOnArc(float currentX, float currentY) { float distance = calDistance(currentX, currentY, centerX, centerY); float degree = calDegreeByPosition(currentX, currentY); return distance > circleRadius - STROKE_WIDTH * 5 && distance < circleRadius + STROKE_WIDTH * 5 && (degree >= -8 && degree <= ARC_FULL_DEGREE + 8); } /** * 根據(jù)當(dāng)前位置,計算出進(jìn)度條已經(jīng)轉(zhuǎn)過的角度。 */ private float calDegreeByPosition(float currentX, float currentY) { float a1 = (float) (Math.atan(1.0f * (centerX - currentX) / (currentY - centerY)) / Math.PI * 180); if (currentY < centerY) { a1 += 180; } else if (currentY > centerY && currentX > centerX) { a1 += 360; } return a1 - (360 - ARC_FULL_DEGREE) / 2; } public void setMax(int max) { this.max = max; invalidate(); } public void setProgress(float progress) { final float validProgress = checkProgress(progress); //動畫切換進(jìn)度值 new Thread(new Runnable() { @Override public void run() { float oldProgress = ProgressBarView1.this.progress; for (int i = 1; i <= 100; i++) { ProgressBarView1.this.progress = oldProgress + (validProgress - oldProgress) * (1.0f * i / 100); postInvalidate(); SystemClock.sleep(20); } } }).start(); } public void setProgressSync(float progress) { this.progress = checkProgress(progress); invalidate(); } //保證progress的值位于[0,max] private float checkProgress(float progress) { if (progress < 0) { return 0; } return progress > max ? max : progress; } public void setDraggingEnabled(boolean draggingEnabled) { this.draggingEnabled = draggingEnabled; } }
(二)ProgressBarView2:
/** * 自定義絢麗的ProgressBar. */ public class ProgressBarView2 extends View { /** * 進(jìn)度條所占用的角度 */ private static final int ARC_FULL_DEGREE = 300; //進(jìn)度條個數(shù) private static final int COUNT = 100; //每個進(jìn)度條所占用角度 private static final float ARC_EACH_PROGRESS = ARC_FULL_DEGREE * 1.0f / (COUNT - 1); /** * 弧線細(xì)線條的長度 */ private int ARC_LINE_LENGTH; /** * 弧線細(xì)線條的寬度 */ private int ARC_LINE_WIDTH; /** * 組件的寬,高 */ private int width, height; /** * 進(jìn)度條最大值和當(dāng)前進(jìn)度值 */ private float max, progress; /** * 繪制弧線的畫筆 */ private Paint progressPaint; /** * 繪制文字的畫筆 */ private Paint textPaint; /** * 繪制文字背景圓形的畫筆 */ private Paint textBgPaint; /** * 圓弧的半徑 */ private int circleRadius; /** * 圓弧圓心位置 */ private int centerX, centerY; public ProgressBarView2(Context context) { super(context); init(); } public ProgressBarView2(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ProgressBarView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { progressPaint = new Paint(); progressPaint.setAntiAlias(true); textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setAntiAlias(true); textBgPaint = new Paint(); textBgPaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (width == 0 || height == 0) { width = getWidth(); height = getHeight(); //計算圓弧半徑和圓心點 circleRadius = Math.min(width, height) / 2; ARC_LINE_LENGTH = circleRadius / 6; ARC_LINE_WIDTH = ARC_LINE_LENGTH / 8; centerX = width / 2; centerY = height / 2; } } private Rect textBounds = new Rect(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float start = (360 - ARC_FULL_DEGREE) >> 1; //進(jìn)度條起始角度 float sweep1 = ARC_FULL_DEGREE * (progress / max); //進(jìn)度劃過的角度 //繪制進(jìn)度條 progressPaint.setColor(Color.parseColor(calColor(progress / max, "#ffff0000", "#ff00ff00"))); progressPaint.setStrokeWidth(ARC_LINE_WIDTH); float drawDegree = 1.6f; while (drawDegree <= ARC_FULL_DEGREE) { double a = (start + drawDegree) / 180 * Math.PI; float lineStartX = centerX - circleRadius * (float) Math.sin(a); float lineStartY = centerY + circleRadius * (float) Math.cos(a); float lineStopX = lineStartX + ARC_LINE_LENGTH * (float) Math.sin(a); float lineStopY = lineStartY - ARC_LINE_LENGTH * (float) Math.cos(a); if (drawDegree > sweep1) { //繪制進(jìn)度條背景 progressPaint.setColor(Color.parseColor("#88aaaaaa")); progressPaint.setStrokeWidth(ARC_LINE_WIDTH >> 1); } canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, progressPaint); drawDegree += ARC_EACH_PROGRESS; } //繪制文字背景圓形 textBgPaint.setStyle(Paint.Style.FILL);//設(shè)置填充 textBgPaint.setColor(Color.parseColor("#41668b")); canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint); textBgPaint.setStyle(Paint.Style.STROKE);//設(shè)置空心 textBgPaint.setStrokeWidth(2); textBgPaint.setColor(Color.parseColor("#aaaaaaaa")); canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint); //上一行文字 textPaint.setTextSize(circleRadius >> 1); String text = (int) (100 * progress / max) + ""; float textLen = textPaint.measureText(text); //計算文字高度 textPaint.getTextBounds("8", 0, 1, textBounds); float h1 = textBounds.height(); canvas.drawText(text, centerX - textLen / 2, centerY - circleRadius / 10 + h1 / 2, textPaint); //分 textPaint.setTextSize(circleRadius >> 3); textPaint.getTextBounds("分", 0, 1, textBounds); float h11 = textBounds.height(); canvas.drawText("分", centerX + textLen / 2 + 5, centerY - circleRadius / 10 + h1 / 2 - (h1 - h11), textPaint); //下一行文字 textPaint.setTextSize(circleRadius / 6); text = "點擊優(yōu)化"; textLen = textPaint.measureText(text); canvas.drawText(text, centerX - textLen / 2, centerY + circleRadius / 2.5f, textPaint); } public void setMax(int max) { this.max = max; invalidate(); } //動畫切換進(jìn)度值(異步) public void setProgress(final float progress) { new Thread(new Runnable() { @Override public void run() { float oldProgress = ProgressBarView2.this.progress; for (int i = 1; i <= 100; i++) { ProgressBarView2.this.progress = oldProgress + (progress - oldProgress) * (1.0f * i / 100); postInvalidate(); SystemClock.sleep(20); } } }).start(); } //直接設(shè)置進(jìn)度值(同步) public void setProgressSync(float progress) { this.progress = progress; invalidate(); } /** * 計算漸變效果中間的某個顏色值。 * 僅支持 #aarrggbb 模式,例如 #ccc9c9b2 */ public String calColor(float fraction, String startValue, String endValue) { int start_a, start_r, start_g, start_b; int end_a, end_r, end_g, end_b; //start start_a = getIntValue(startValue, 1, 3); start_r = getIntValue(startValue, 3, 5); start_g = getIntValue(startValue, 5, 7); start_b = getIntValue(startValue, 7, 9); //end end_a = getIntValue(endValue, 1, 3); end_r = getIntValue(endValue, 3, 5); end_g = getIntValue(endValue, 5, 7); end_b = getIntValue(endValue, 7, 9); return "#" + getHexString((int) (start_a + fraction * (end_a - start_a))) + getHexString((int) (start_r + fraction * (end_r - start_r))) + getHexString((int) (start_g + fraction * (end_g - start_g))) + getHexString((int) (start_b + fraction * (end_b - start_b))); } //從原始#AARRGGBB顏色值中指定位置截取,并轉(zhuǎn)為int. private int getIntValue(String hexValue, int start, int end) { return Integer.parseInt(hexValue.substring(start, end), 16); } private String getHexString(int value) { String a = Integer.toHexString(value); if (a.length() == 1) { a = "0" + a; } return a; } }
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android視圖View技巧總結(jié)》、《Android開發(fā)動畫技巧匯總》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android布局layout技巧總結(jié)》、《Android開發(fā)入門與進(jìn)階教程》、《Android資源操作技巧匯總》及《Android控件用法總結(jié)》
希望本文所述對大家Android程序設(shè)計有所幫助。
- Android動態(tài)自定義圓形進(jìn)度條
- Android編程之ProgressBar圓形進(jìn)度條顏色設(shè)置方法
- Android帶圓形數(shù)字進(jìn)度的自定義進(jìn)度條示例
- Android實現(xiàn)帶數(shù)字的圓形進(jìn)度條(自定義進(jìn)度條)
- Android編程實現(xiàn)WebView添加進(jìn)度條的方法
- Android自定義控件實現(xiàn)圓形進(jìn)度條
- Android自定義Material進(jìn)度條效果
- Android自定義view實現(xiàn)進(jìn)度條指示效果
- android自定義view制作圓形進(jìn)度條效果
- Android實現(xiàn)環(huán)形進(jìn)度條的實例
- Android編程實現(xiàn)類似于圓形ProgressBar的進(jìn)度條效果
相關(guān)文章
android非RxJava環(huán)境下使用Handler實現(xiàn)預(yù)加載
這篇文章主要介紹了android非RxJava環(huán)境下使用Handler實現(xiàn)預(yù)加載的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01Presenting?Streams?in?Flutter小技巧
這篇文章主要為大家介紹了Presenting?Streams?in?Flutter小技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android人臉識別Demo豎屏YUV方向調(diào)整和圖片保存(分享)
下面小編就為大家分享一篇Android人臉識別Demo實現(xiàn)豎屏YUV方向調(diào)整和圖片保存的方法。具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Android開發(fā)使用Messenger及Handler進(jìn)行通信的方法示例
這篇文章主要介紹了Android開發(fā)使用Messenger及Handler進(jìn)行通信的方法,結(jié)合實例形式分析了Android使用Messenger及Handler定義客戶端與服務(wù)器端實現(xiàn)通信的相關(guān)操作技巧,需要的朋友可以參考下2017-12-12Android中RecyclerView實現(xiàn)多級折疊列表效果(TreeRecyclerView)
RecyclerView出現(xiàn)已經(jīng)有一段時間了,相信大家肯定不陌生了,下面這篇文章主要給大家介紹了Android中RecyclerView實現(xiàn)多級折疊列表效果(TreeRecyclerView)的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下。2017-05-05Android自定義view仿QQ的Tab按鈕動畫效果(示例代碼)
這篇文章主要介紹了Android自定義view仿QQ的Tab按鈕動畫效果(示例代碼),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考價值,需要的朋友可以參考下2021-01-01Android動畫之TranslateAnimation用法案例詳解
這篇文章主要介紹了Android動畫之TranslateAnimation用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08