
在初探自定義View(一)和初探自定義View(二)里面,我們都是針對(duì)系統(tǒng)現(xiàn)有的組件進(jìn)行設(shè)計(jì)和修改,那萬一碰到需要自己根據(jù)要求來設(shè)計(jì)一個(gè)全新的View,這就是今天要探討的主要內(nèi)容---自繪控件。
自定義View的三個(gè)過程分別是onMeasure()、onLayout()和onDraw(),在今天這個(gè)DEMO里因?yàn)樯婕暗綄⒆远xView和系統(tǒng)控件的組合使用,所以必須重寫onMeasure()方法來確保自定義View的width和height能夠根據(jù)我們的設(shè)定呈現(xiàn)出來(具體原因和方法參見初探自定義View(一)的相關(guān)內(nèi)容),否則無論設(shè)定width和height為wrap_content或確切值自定義View都會(huì)滿屏,系統(tǒng)控件就無法顯示出來(真啰嗦)。
下面正式進(jìn)入正題,巧婦難為無米之炊,自定義View的第一步往往都是把畫筆顏料準(zhǔn)備好。
1.初始化
public void init(){
mCirclePaint=new Paint();
mCirclePaint.setColor(Color.RED);
mCirclePaint.setStyle(Paint.Style.FILL);
mArcPaint=new Paint();
mArcPaint.setColor(Color.BLACK);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(100);
mTextPaint=new Paint();
mTextPaint.setColor(Color.BLUE);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextSize(50);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mShowSize=mTextPaint.getTextSize();
Log.d("dd","size is"+mShowSize);
}
@Override
protected void onSizeChanged(int w,int h,int oldW,int oldH){
super.onSizeChanged(w,h,oldW,oldH);
length = w;
x = length/2;
r = (float) (length*0.5/2);
}
我們把這個(gè)View分成了三個(gè)部分---圓、弧線和文本,所以這邊初始化了三種畫筆,mShowSize是為之后給文本定位做準(zhǔn)備,x和r分別是圓的圓心坐標(biāo)和半徑,這樣圓就確定了。
2.開始繪制
public void onDraw(Canvas canvas){
super.onDraw(canvas);
rectF=new RectF((float)(length*0.1),
(float)(length*0.1),
(float)(length*0.9),
(float)(length*0.9));
canvas.drawCircle(x,x,r,mCirclePaint);
canvas.drawArc(rectF,0,mSweepValue,false,mArcPaint);//若為true則為扇形
/*canvas.drawText(mShowText,0,mShowText.length(),x,x+(mShowSize/4),mTextPaint);*///
/*canvas.drawText(mShowText,0,mShowText.length(),x-(width/2),x+(mShowSize/4),mTextPaint);*/
//0是index
canvas.drawText(mShowText,0,mShowText.length(),x,x+(mShowSize/4),mTextPaint);
}
確定了圓心坐標(biāo)和半徑就能畫圓了,而確定了圓弧的外接矩陣的坐標(biāo)就能確定圓弧的位置,具體掃過的角度可通過參數(shù)來設(shè)定,這兩個(gè)用法都比較容易理解。canvas.drawText()是玄機(jī)就多了,系統(tǒng)默認(rèn)繪制文本的x坐標(biāo)是從文本的左下角開始的,這里由于之前我們把文本的重心設(shè)定在的中點(diǎn)mTextPaint.setTextAlign(Paint.Align.CENTER);,如果把文本看成是一條線段的話,那么x就是指線段重點(diǎn)的坐標(biāo),而y坐標(biāo)是文本bottom的位置,如下示意圖:
3.上強(qiáng)度
由于這個(gè)DEMO經(jīng)常被用于顯示一個(gè)百分比的比例,所以這里就加一個(gè)動(dòng)態(tài)地改變這個(gè)比例和UI的功能,先上布局文件:
<com.example.view.PPT
android:id="@+id/circle"
android:layout_width="400dp"
android:layout_height="400dp" />
<EditText
android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="提交"/>
經(jīng)過測(cè)試,把自定義View的寬高設(shè)定為400dp的時(shí)候能夠較好的顯示和系統(tǒng)組件(太大的話會(huì)把系統(tǒng)組件擠出去),簡單地用EditText和編輯比例,Button來向View提交這個(gè)比例并重繪View,說到重繪View,那必然有一個(gè)監(jiān)聽比例參數(shù)變化的方法,話不多說,上代碼:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PPT circle=(PPT) findViewById(R.id.circle);
okc=Integer.parseInt(editText.getText().toString());
circle.setSweepValue(okc);
}
});
public void setSweepValue(int sweepValue){
float z= (float)sweepValue;
Log.d("dd","z="+sweepValue);
if (z!=0){
mSweepValue=(float) (360.0*(sweepValue/100.0));
mShowText=sweepValue+"%";
Log.d("dd",""+sweepValue);
}else {
mSweepValue=90;
mShowText=25+"%";
}
this.invalidate();
}
點(diǎn)擊提交按鈕之后,會(huì)把比例值okc通過View暴露出來的方法setSweepValue()來傳入這個(gè)值,最后調(diào)用this.invalidate()方法重繪View。
前面提到的重寫onMeasure():
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
setMeasuredDimension(
measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec)
);
}
private int measureWidth(int measureSpec){
int result=0;
int Mode= View.MeasureSpec.getMode(measureSpec);
int Size= View.MeasureSpec.getSize(measureSpec); //自己考慮后的尺寸,若是wrap_content 那就是全屏
//Size=1080 是像素
if (Mode== View.MeasureSpec.EXACTLY){
result=Size;
}else {
result=200;
if (Mode== View.MeasureSpec.AT_MOST){
result=Math.min(result,Size);
}
}
Log.d("tag","widthsize is " + result);
return result;
}
private int measureHeight(int measureSpec){
int result=0;
int Mode= View.MeasureSpec.getMode(measureSpec);
int Size= View.MeasureSpec.getSize(measureSpec);
if (Mode== View.MeasureSpec.EXACTLY){
result=Size;
Log.d("tag","size is " + result);
}else {
result=200;
if (Mode== View.MeasureSpec.AT_MOST){
result=Math.min(result,Size);
}
}
/*Log.d("tag","heightsize is " + result);*/
return result;
}
具體原理這里就不多bb了,詳情見初探自定義View(一)。
最后上一下效果圖:

溜了溜了,吃波牛蛙美滋滋