Android自定義View實現(xiàn)圓弧進度的效果
前言
Android開發(fā)中,常常自定義View實現(xiàn)自己想要的效果,當然自定義View也是Android開發(fā)中比較難的部分,涉及到的知識有Canvas(畫布),Paint(畫筆)等,自定義控件分為三種:一是直接繼承自View,完全的自定義;二是在原有控件的基礎(chǔ)上進行改造,達到自己想要的效果;還有一種就是自定義組合控件,將已有的控件根據(jù)自己的需要進行組合實現(xiàn)的效果。本人對自定義View也是一知半解,簡單記錄下自己學(xué)習(xí)自定義View(繼承自View)的過程,方便日后翻閱。
使用技術(shù)
1、繼承View
2、Canvas
3、paint
效果圖:
1.分析組件
自定義view首先我們要分析組件是由幾部分組成,然后在依次順序使用canvas畫出組件,首先可以看出該組件由一個背景外部圓,一個圓弧,以及圓弧端點是由兩個圓組成,內(nèi)部是三個文字。分析完畢,我們就可以先定義組件屬性了
2.組件屬性
1.在values目錄下新建attrs.xml文件,用來編寫組件屬性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ProgressView"> <attr name="title" format="string"/> <attr name="num" format="string"/> <attr name="unit" format="string"/> <attr name="titleTextsize" format="dimension"/> <attr name="numTextsize" format="dimension"/> <attr name="unitTextsize" format="dimension"/> <attr name="titleTextColor" format="color"/> <attr name="numTextColor" format="color"/> <attr name="unitTextColor" format="color"/> <attr name="backCircleWidth" format="dimension"/> <attr name="outerCircleWidth" format="dimension"/> <attr name="backCircleColor" format="color"/> <attr name="outerCircleColor" format="color"/> <attr name="endCircleWidth" format="dimension"/> <attr name="edgeDistance" format="dimension"/> <attr name="endCircleColor" format="color"/> <attr name="currentPercent" format="float"/> </declare-styleable> </resources>
2.自定義view繼承View并實現(xiàn)構(gòu)造方法
public class ProgressView extends View { /** * 在java代碼里new的時候會用到 * @param context */ public ProgressView(Context context) { super(context); init(context, null); } /** * 在xml布局文件中使用時自動調(diào)用 * @param context */ public ProgressView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context,attrs); } /** * 不會自動調(diào)用,如果有默認style時,在第二個構(gòu)造函數(shù)中調(diào)用 * @param context * @param attrs * @param defStyleAttr */ public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
2.初始化屬性
/** * 初始化屬性 * @param context * @param attrs */ private void init(Context context,AttributeSet attrs){ this.mContext = context; if(attrs!=null){ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView); title = array.getString(R.styleable.ProgressView_title); num = array.getString(R.styleable.ProgressView_num); unit = array.getString(R.styleable.ProgressView_unit); titleTextsize = array.getDimension(R.styleable.ProgressView_titleTextsize,24); numTextsize = array.getDimension(R.styleable.ProgressView_numTextsize,48); unitTextsize = array.getDimension(R.styleable.ProgressView_unitTextsize,24); titleTextColor = array.getColor(R.styleable.ProgressView_titleTextColor, Color.parseColor("#656d78")); numTextColor = array.getColor(R.styleable.ProgressView_numTextColor, Color.parseColor("#4fc1e9")); unitTextColor = array.getColor(R.styleable.ProgressView_unitTextColor, Color.parseColor("#4fc1e9")); backCircleWidth = array.getDimension(R.styleable.ProgressView_backCircleWidth, 12); outerCircleWidth = array.getDimension(R.styleable.ProgressView_outerCircleWidth, 20); backCircleColor = array.getColor(R.styleable.ProgressView_backCircleColor, Color.parseColor("#e6e9ed")); outerCircleColor = array.getColor(R.styleable.ProgressView_outerCircleColor, Color.parseColor("#4fc1e9")); endCircleWidth = array.getDimension(R.styleable.ProgressView_endCircleWidth,24); endCircleColor = array.getColor(R.styleable.ProgressView_endCircleColor, Color.parseColor("#4fc1e9")); edgeDistance = array.getDimension(R.styleable.ProgressView_edgeDistance, 12); currentPercent = array.getFloat(R.styleable.ProgressView_currentPercent, 0); if(currentPercent>1||currentPercent<0){ currentPercent = currentPercent>1?1:0; } //初始化畫筆 backCirclePaint = new Paint(); backCirclePaint.setAntiAlias(true); backCirclePaint.setStrokeWidth(backCircleWidth); backCirclePaint.setColor(backCircleColor); backCirclePaint.setStyle(Paint.Style.STROKE); outerCirclePaint = new Paint(); outerCirclePaint.setAntiAlias(true); outerCirclePaint.setStrokeWidth(outerCircleWidth); outerCirclePaint.setColor(outerCircleColor); outerCirclePaint.setStyle(Paint.Style.STROKE); endBigCirclePaint = new Paint(); endBigCirclePaint.setAntiAlias(true); endBigCirclePaint.setStrokeWidth(endCircleWidth); endBigCirclePaint.setColor(endCircleColor); endBigCirclePaint.setStyle(Paint.Style.STROKE); endSmallCirclePaint = new Paint(); endSmallCirclePaint.setAntiAlias(true); endSmallCirclePaint.setColor(Color.WHITE); endSmallCirclePaint.setStyle(Paint.Style.FILL); titlePaint = new Paint(); //通過設(shè)置Flag來應(yīng)用抗鋸齒效果 titlePaint.setFlags(Paint.ANTI_ALIAS_FLAG); titlePaint.setAntiAlias(true); //設(shè)置文字居中 //titlePaint.setTextAlign(Paint.Align.CENTER); titlePaint.setColor(titleTextColor); titlePaint.setTextSize(titleTextsize); numPaint = new Paint(); numPaint.setAntiAlias(true); numPaint.setFlags(Paint.ANTI_ALIAS_FLAG); //設(shè)置文字居中 //numPaint.setTextAlign(Paint.Align.CENTER); numPaint.setColor(numTextColor); numPaint.setTextSize(numTextsize); unitPaint = new Paint(); unitPaint.setAntiAlias(true); unitPaint.setFlags(Paint.ANTI_ALIAS_FLAG); //unitPaint.setTextAlign(Paint.Align.CENTER); unitPaint.setColor(unitTextColor); unitPaint.setTextSize(unitTextsize); //釋放 array.recycle(); } }
3.獲取組件高度寬度,重寫onMeasure方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ///獲取總寬度,是包含padding值 //處理WAP_CONTENT int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { //默認大小 200*200 setMeasuredDimension(200,200); }else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(height, height); }else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(width, width); } }
4.重寫onDraw()繪制組件各部分
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //圓心 int centerX = width / 2; int centerY = height / 2; //計算半徑 float radius = centerX - edgeDistance; //畫背景圓 drawBackCircle(canvas,centerX,centerY,radius); //繪制圓弧進度 drawProgress(canvas,centerX,centerY); //繪制標題 drawText(canvas); }```
###### 4.1繪制背景圓
```java /** * 繪制背景圓 * @param canvas * @param x 圓心位置x * @param y 圓心位置y * @param radius 半徑 */ private void drawBackCircle(Canvas canvas,int x,int y,float radius){ canvas.drawCircle(x,y,radius,backCirclePaint); }
4.2 繪制圓弧進度
1.注意:圓弧上端點進度為0或者100不顯示,此外端點的位置使用sin和cos來確定坐標;
/** * 繪制圓弧進度 */ private void drawProgress(Canvas canvas,int x,int y){ //圓弧的范圍 RectF rectF = new RectF(edgeDistance, edgeDistance, width - edgeDistance, height - edgeDistance); //定義的圓弧的形狀和大小的范圍 // 置圓弧是從哪個角度來順時針繪畫的 //設(shè)置圓弧掃過的角度 //設(shè)置我們的圓弧在繪畫的時候,是否經(jīng)過圓形 這里不需要 //畫筆 canvas.drawArc(rectF, -90, 360 * currentPercent, false, outerCirclePaint); //繪制端圓 //進度在0~100%的時候才會畫終點小圓,可以自由改動 if(currentPercent>0&¤tPercent<1){ //繪制外層大圓 canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180), y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 2, endBigCirclePaint); //繪制內(nèi)層圓點 canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180), y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 4, endSmallCirclePaint); } }
4.3 繪制文字
/** * 繪制標題 * @param canvas */ private void drawText(Canvas canvas) { Rect textRect = new Rect(); //返回的則是當前文本所需要的最小寬度,也就是整個文本外切矩形的寬度 titlePaint.getTextBounds(title, 0, title.length(), textRect);//25 50 175 //高度平分四部分 float h = height/ 4; //文字居中 canvas.drawText(title, width / 2 - textRect.width() / 2, h + textRect.height() / 2, titlePaint); numPaint.getTextBounds(num, 0, num.length(), textRect); canvas.drawText(num, width / 2 - textRect.width() / 2, h*2 + textRect.height() / 2, numPaint); unitPaint.getTextBounds(unit, 0, unit.length(), textRect); canvas.drawText(unit, width / 2 - textRect.width() / 2, 3*h + textRect.height() / 2, unitPaint); }
4.4提供外部修改進度方法以及進度過度
/** * 設(shè)置進度 */ public void setProgress(final float progress){ new Thread(new Runnable() { @Override public void run() { for(int i=0;i<=progress*100;i++){ Message msg = new Message(); msg.what = 1; msg.obj = i; try { Thread.sleep(20); handler.sendMessage(msg); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } private Handler handler = new Handler(new Handler.Callback(){ @Override public boolean handleMessage(@NonNull Message msg) { if(msg.what==1){ currentPercent = ((float)Integer.valueOf(msg.obj+"")/100); System.out.println("更新"+currentPercent); invalidate(); } return false; } });
總結(jié):使用Canvas的drawArc方法繪制圓弧及drawText繪制文本信息等;
github地址:項目地址
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android OpenGL ES 實現(xiàn)抖音傳送帶特效(原理解析)
這篇文章主要介紹了Android OpenGL ES 實現(xiàn)抖音傳送帶特效,抖音傳送帶特效推出已經(jīng)很長一段時間了,前面也實現(xiàn)了下,最近把它整理出來了,如果你有仔細觀測傳送帶特效,就會發(fā)現(xiàn)它的實現(xiàn)原理其實很簡單,需要的朋友可以參考下2022-07-07android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式示例
本篇文章主要介紹了android效果TapBarMenu繪制底部導(dǎo)航欄的使用方式,具有一定的參考價值,有興趣的可以了解一下。2017-01-01使用Android Studio實現(xiàn)為系統(tǒng)級的app簽名
這篇文章主要介紹了使用Android Studio實現(xiàn)為系統(tǒng)級的app簽名,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03詳解Android studio ndk配置cmake開發(fā)native C
這篇文章主要介紹了詳解Android studio ndk配置cmake開發(fā)native C,非常具有實用價值,需要的朋友可以參考下2017-09-09Android 中對JSON數(shù)據(jù)解析實例代碼
這篇文章主要介紹了Android 中對JSON數(shù)據(jù)解析實例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03Android中SurfaceView和view畫出觸摸軌跡
這篇文章主要介紹了Android中SurfaceView和view畫出觸摸軌跡的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03Android itemDecoration接口實現(xiàn)吸頂懸浮標題
這篇文章主要介紹了Android中使用itemdecoration實現(xiàn)吸頂懸浮標題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-11-11