上篇文章通过继承view定义了一个圆形控件,在界面上绘制一个圆形,并且根据不同的测量模式设置了不同的大小:自定义控件(一) 。但是我们定义的圆形在界面设计时半径、颜色都已经固定了,本文主要总结如何像原生控件一样,通过在xml文件中设置属性来控制圆形的半径以及颜色,并在圆形中心显示一段文字
首先要定义控件的属性名称,在values文件夹中新建attrs.xml文件,声明属性的名称和类型:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="myView">
<attr name="Text" format="string"/>
<attr name="TextColor" format="color"/>
<attr name="CircleColor" format="color"/>
<attr name="Radius" format="dimension"/>
<attr name="TextSize" format="dimension"/>
</declare-styleable>
</resources>
系统提供了TypedArray数据结构来获取自定义属性值,通过TypedArray对象的getString()、getColor()等方法就可以获得xml中设置的属性值,需要注意的是获取完属性值后需要调用TypedArray的recycle方法来完成资源的回收:
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.myView);
mCircleText = ta.getString(R.styleable.myView_Text);
mCircleColor = ta.getColor(R.styleable.myView_CircleColor,0);
mTextColor = ta.getColor(R.styleable.myView_TextColor,0);
radius = ta.getDimension(R.styleable.myView_Radius,50);
mTextSize = ta.getDimension(R.styleable.myView_TextSize,15);
ta.recycle();
获取到属性之后,通过Paint 的setcolor、setTextSize等函数定义画笔,就可以在屏幕上进行绘制了,我们要实现在圆形绘制一段文字,所以在onDraw中绘制完圆形后,通过调用drawText方法绘制文字,使用这个方法时需要注意参数的含义:
canvas.drawText(text,x,y,paint)
(1)text: 表示要绘制的文字内容
(2)x:表示对齐位置,通过调用paint的setTextAlign(Align align)方法,设置文字的对齐标准,Paint.Align.Center:以中心对齐,Paint.Align.LEFT以左边界对齐,Paint.Align.RIGHT以右边界对齐。
(3)y:表示字符baseline 的位置,这里需要特别注意,y不表示文字中心的高度,而表示文字基线的高度,基线是位于文字下方的,就像在直线上写英文字母一样,那条横线叫做基线。
(2)paint:是绘制的画笔
我们在以圆心位置对齐,绘制文字,所以x以中心对齐,设置宽对齐位置为圆心的位置,若要使文字的中心和圆心重合,需要使文字的基线在圆心下方Text.height()/2的高度才能保证,通过获取问题的外接矩形就可以方便的知道文字的高,从而设置y的大小:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
Rect bounds = new Rect();
mTextPaint.getTextBounds(mCircleText,0,mCircleText.length(),bounds);
canvas.drawCircle(width/2,height/2,radius,mPaint);
canvas.drawText(mCircleText,width/2,height/2+bounds.height()/2,mTextPaint);
}
这样就可以通过xml设置属性,来改变控件的大小、颜色以及文字,但是在设置前,一定要记得引用第三方空间的名字空间,在布局文件中可以看到:
xmlns:android="http://schemas.android.com/apk/res/android
xmlns:custom="http://schemas.android.com/apk/res-auto"
<my.project.ViewTest.myView
android:layout_width="match_parent"
android:layout_height="200dp"
custom:Text="my circle"
custom:TextColor="#33ff00"
custom:Radius="50dp"
custom:CircleColor="#993300"
custom:TextSize="20sp"
/>
效果图如下:
myView源码:
public class myView extends View {
private int mCircleColor;
private String mCircleText = "defuat";
private int mTextColor;
private float mTextSize;
private TypedArray ta ;
private float radius = 50;
private Paint mPaint,mTextPaint;
public myView(Context context) {
super(context);
init(context);
}
public myView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ta = context.obtainStyledAttributes(attrs,R.styleable.myView);
init(context);
}
public myView(Context context, AttributeSet attrs) {
super(context, attrs);
ta = context.obtainStyledAttributes(attrs,R.styleable.myView);
init(context);
}
private void init(Context context){
mCircleText = ta.getString(R.styleable.myView_Text);
mCircleColor = ta.getColor(R.styleable.myView_CircleColor,0);
mTextColor = ta.getColor(R.styleable.myView_TextColor,0);
radius = ta.getDimension(R.styleable.myView_Radius,50);
mTextSize = ta.getDimension(R.styleable.myView_TextSize,15);
mPaint = new Paint();
mTextPaint = new Paint();
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(mTextSize);
mPaint.setColor(mCircleColor);
mTextPaint.setColor(mTextColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
Rect bounds = new Rect();
mTextPaint.getTextBounds(mCircleText,0,mCircleText.length(),bounds);
canvas.drawCircle(width/2,height/2,radius,mPaint);
canvas.drawText(mCircleText,width/2,height/2+bounds.height()/2,mTextPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMeasured,heightMeasured;
widthMeasured = measureWidth(widthMeasureSpec);
heightMeasured = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthMeasured,heightMeasured);
}
private int measureHeight(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 200;
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
private int measureWidth(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 200;
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
}