項目中有時候我們為了博得用戶的眼球,需要自定義一些好看的控件,下面記錄一個自定義帶進度值的圓形進度條
先上效果

下面記錄具體的實現過程
在Android studio下新建一個PRoject,然后新建一個CircleProgressView,繼承系統的view,然后重寫它的三個構造方法,如下:
public CircleProgressView(Context context) { this(context,null); } public CircleProgressView(Context context, AttributeSet attrs) { this(context, attrs,0); } public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; }然后在定義圓形角度條需要的一些參數: private Context mContext; private Paint mBackgroudPaint;//圓的畫筆 private Paint mFrontPaint;//進度條的畫筆 private Paint mTextPaint;//數字的畫筆 private int mWidth;//控件的寬度 private int mHeight;//控件的高度 private float redis = 200;//默認圓的半徑 private float roundWidth = 50;//默認圓形進度的寬度 private float textSize = 50;//默認字體的大小 private int progress = 0;//記錄進度值 private int maxProgress = 100;//最大進度值 private int backProgressColor = 0xff778899;//默認背景的顏色 private int roundProgressColor = 0xff00BFFF;//默認圓形進度條的顏色 private int textColor = 0xff008080;//默認字體的顏色 private int mXCenter;//圓心的x坐標 private int mYCenter;//圓心的Y坐標我們先不自定義屬性,先在代碼中使用默認的值。寫一個方法,得到手機的中心點,我們要以這個中心點繪制圓 /** * 得到屏幕的中心點 */ private void getPhoneCenter(){ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); mXCenter = wm.getDefaultDisplay().getWidth()/2; mYCenter = wm.getDefaultDisplay().getHeight()/2; }然后在構造方法中調用getPhoneCenter()方法,獲取到中心點的坐標。得到中心點的坐標后,就可以開始繪制了,重寫onDraw()方法,在onDraw()方法中,我們分別繪制背景圓,進度條的扇形,和進度值的數字
先繪制背景圓:
mBackgroudPaint = new Paint();//初始化背景畫筆 mBackgroudPaint.setColor(backProgressColor); mBackgroudPaint.setAlpha(100);//透明度 0~255 從完全透明到不透明 mBackgroudPaint.setAntiAlias(true);//消除鋸齒 mBackgroudPaint.setStyle(Paint.Style.FILL);//實心圓 canvas.drawCircle(mXCenter,mYCenter,redis,mBackgroudPaint);//制定中心點的坐標,半徑然后在繪制進度值的數字 mTextPaint = new Paint();//初始化數字的畫筆 mTextPaint.setColor(textColor);// mTextPaint.setAntiAlias(true);//消除鋸齒 mTextPaint.setTextSize(textSize);// mTextPaint.setTextAlign(Paint.Align.CENTER);//設置文字的對齊方式 canvas.drawText(progress+"%",mXCenter,mYCenter, mTextPaint);//progress是加載的進度值最后就是繪制進度條了 mFrontPaint = new Paint(); mFrontPaint.setColor(roundProgressColor); mFrontPaint.setAntiAlias(true);//消除鋸齒 mFrontPaint.setStyle(Paint.Style.STROKE);//空心圓 mFrontPaint.setStrokeWidth(roundWidth); mFrontPaint.setAlpha(200);//透明度 //確定一個矩形的區域 RectF oval = new RectF(mXCenter-redis+roundWidth/2,mYCenter-redis+roundWidth/2,mXCenter+redis-roundWidth/2,mYCenter+redis-roundWidth/2); progress = (int)((progress*1.0/maxProgress)*360); /** * 繪制扇形 * 第一個參數:確定的矩形區域 * 第二個參數:開始繪制的角度,從12點中開始繪制,角度為-90° * 第三個參數:是否把弧形的2個點連接起來,false:不連接 * 第四個參數:畫筆 */ canvas.drawArc(oval,-90,progress,false,mFrontPaint);然后我們在MainActivity對應的xml文件中添加自定義的CircleProgressView控件(此處的代碼省略)。我們在MainActivity中啟用一個線程來模擬下載,并結合Handle來處理模擬的結果class MyThread extends Thread{ @Override public void run() { for (int i=0;i<100;i++){ Message msg = myHandler.obtainMessage(); msg.what = UPDATE_PROGRESS; msg.obj = i+1; myHandler.sendMessage(msg); try { Thread.sleep(100);//休眠100ms } catch (InterruptedException e) { e.printStackTrace(); } } } }Handle接受到每次接受到消息以后,調用CircleProgressView中的setProgress方法,設置進度的變化private final int UPDATE_PROGRESS = 001; CircleProgressView circleProgressView = null; Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 001: int progress =(int) msg.obj; circleProgressView.setProgress(progress); break; } } };那我們就要在CircleProgressView中給progress添加set,get方法,在set方法中,判斷當前的progress是否大于最大值,如果不大于,在重新繪制viewpublic void setProgress(int progress) { this.progress = progress; if(progress<=maxProgress){//判斷progress是否大于maxProgress invalidate();//調用這個方法,會重新繪制 } } public int getProgress() { return progress; }先看一下效果:
需要注意的是:這個時候關于圓形進度控件的屬性我們都是在代碼中寫死的,這樣肯定不能達到復用的目的,我們需要把其中一些關鍵的屬性抽取出來,像系統控件的屬性那樣可以在xml中配置
在res/value文件下新建一個attrs.xml的樣式文件,定義以下屬性:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="RoundProgressBar"> <attr name="roundColor" format="color"/> <!-- 背景色--> <attr name="roundProgressColor" format="color"/><!-- 進度條的顏色--> <attr name="roundWidth" format="dimension"></attr><!-- 進度條的寬度--> <attr name="textColor" format="color" /><!-- 字體的顏色--> <attr name="textSize" format="dimension" /><!-- 字體的大小--> </declare-styleable></resources>然后我們在CircleProgressView的構造函數中獲取這些屬性,并且賦值。自定義一個方法,initResource(attrs)獲取自定義的屬性值,并在構造函數中調用
private void initResource(AttributeSet attrs) { //獲取我們自定義的文件名,RoundProgressBar是attrs中定義的name TypedArray mTypeArray = mContext.obtainStyledAttributes(attrs,R.styleable.RoundProgressBar,0,0); //獲取背景色,如果沒有設置則使用默認 backProgressColor = mTypeArray.getColor(R.styleable.RoundProgressBar_roundColor,backProgressColor); //獲取進度條顏色,如果沒有設置則使用默認 roundProgressColor = mTypeArray.getColor(R.styleable.RoundProgressBar_roundProgressColor,roundProgressColor); //獲取背景圓的半徑,如果沒有設置則使用默認 redis = mTypeArray.getDimension(R.styleable.RoundProgressBar_roundWidth,redis); //進度條的寬度設置為半徑的1/5 roundWidth = redis/5; //獲取文本的顏色,如果沒有設置則使用默認 textColor = mTypeArray.getColor(R.styleable.RoundProgressBar_textColor,textColor); //獲取文本的大小,如果沒有設置則使用默認 textSize = mTypeArray.getDimension(R.styleable.RoundProgressBar_textSize,textSize); }然后我們重寫onMeasure()方法,測量控件的大小 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getRealSize(widthMeasureSpec); mHeight = getRealSize(heightMeasureSpec); }自定義一個getRealSize()的方法,得到控件的大小 private int getRealSize(int measureSpec){ float res = -1; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); if(mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED){ res = redis*2; }else{ res = size; } return (int)res; }到此代碼寫的已經差不多了。這兒需要注意:在eclipse中使用自定義屬性,我們要加上
xmlns:app="http://schemas.android.com/apk/res/我們自己定義的view的包名"但是在Android studio中使用的時候這么寫會報錯,提示找不到,我們直接讓它自動搜索,需要這么寫xmlns:app ="http://schemas.android.com/apk/res-auto"最后就是在xml中對自定義屬性的使用了
<main.zhaocd.com.circleprogressview.CircleProgressView android:id="@+id/my_view" android:layout_width="600dp" android:layout_height="600dp" app:roundWidth="80dp" app:roundColor="#7f8d9c" app:roundProgressColor="#07ce7e" app:textColor="#e21e17" app:textSize="40dp" />最后在總結一下,一般自定義view的步驟:1、創建attrs文件,自定義屬性(如果一開始不能很好的把握需要那些屬性,可以把這一步放到最后)
2、在構造方法中獲取自定義的屬性
3、重寫onMeasure()方法(測量控件的大小)
4、重寫onDraw()方法(具體的繪制,調用invalidate()方法會強制執行onDraw方法)
所有的源代碼下載
新聞熱點
疑難解答