自定义View的3种形式:自绘控件(继承View)、组合控件(这个最没意思了)、以及继承控件(继承已经有的控件)
我们想自定义View,只需按照以下几步进行即可:
1)自定义一个View,继承View或者View的子类(如TextView,非ViewGroup)
2)在View的方法中获得我们自定义的属性(如果需要自定义属性的话)
3)重写onMeasure(此方法非必写,大多数情况下还是需要重写的)
4)重写onDraw
注:自定义View只需重写onMeasure和onDraw
1.自定义属性:在res/values下创建attrs文件,声明属性,format中的属性如string代表字符串,color代表颜色等等
<declare-styleable name="CustomView">
<attr name="CustomText" format="string"></attr>
<attr name="CustomTextColor" format="color"></attr>
<attr name="CustomTextSize" format="dimension"></attr>
</declare-styleable>
2.在布局文件中引用我们自定义的属性:2.在布局文件中引用我们自定义的属性:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cuiboye="http://schemas.android.com/apk/res/com.example.customview"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.customview.CustomView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
cuiboye:CustomText="我的"
cuiboye:CustomTextColor="#ffffff"
cuiboye:CustomTextSize="16sp" />
</RelativeLayout>
命名空间: xmlns:cuiboye="http://schemas.android.com/apk/res/com.example.customview"
3.自定义View,在构造方法中获得我们自定义的属性:
public class CustomView extends View {
private String customText;
private int customTextSize;
private int customTextColor;
private Paint paint;
private Rect rect;
public CustomView(Context context) {
this(context,null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//通过TypedArray获得自定义的属性,AttributeSet中保存的是该View在布局中声明的所有的属性
//AttributeSet保存的是该View在布局中声明的所有的属性,但是TypedArray不能抛弃,因为TypedArray简化了解析过程(如果布局中的值是包含@+的字符串,解析的值我们看不懂)
//比如下面的
//android:layout_height="@dimen/dp200"
//zhy:text="@string/hello_world"
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView,defStyleAttr,0);//得到自定义的属性
int indexCount = typedArray.getIndexCount();//此处得到的是自定义属性的数量,不是布局中所有的属性
for(int i=0;i<indexCount;i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.CustomView_CustomText:
customText = typedArray.getString(attr);
break;
case R.styleable.CustomView_CustomTextColor:
customTextColor = typedArray.getColor(attr, Color.RED);
break;
case R.styleable.CustomView_CustomTextSize:
customTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle(); //TypedArray使用完后一定要释放
int number = attrs.getAttributeCount();//此处得到的是布局中所有的属性的数量
//为字画一个矩形
paint = new Paint();
paint.setTextSize(customTextSize);
rect = new Rect();
paint.getTextBounds(customText,0,customText.length(), rect);
}
/**重写onMeasure
* 测量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height ;
if (widthMode == MeasureSpec.EXACTLY)
{
width = widthSize;
} else
{
paint.setTextSize(customTextSize);
paint.getTextBounds(customText, 0, customText.length(), rect);
float textWidth = rect.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else
{
paint.setTextSize(customTextSize);
paint.getTextBounds(customText, 0, customText.length(), rect);
float textHeight = rect.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}
/**重写onDraw
* 绘制
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLUE);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);//画一个矩形的画布
paint.setColor(customTextColor);
canvas.drawText(customText,getWidth()/2 - rect.width()/2,getHeight()/2 + rect.height()/2,paint);//在画布上画字
}
}
最后,给出一些容易误解的地方:
如果系统中已经有了语义比较明确的属性(android本身的一些属性),我们也可以直接使用
1)attrs中这样写 android:text
2) 布局中这样写 android:text="Hello"
3)自定义View中这样写 typedArray.getString(R.styleable.CustomView_android_text)