如标题所见,最近我自己在网上看了很多资料来学习自定义控件。。自定义View对很多人来说还是有些难度的,我希望我的博客能帮助到一些人,这样写的话对自己巩固知识也有很大的帮助,我们先来总结一下自定义View的步骤。
1,考虑清楚自己的控件需要做到什么事情需要什么属性,如自定义一个圆形滚动条需要考虑颜色,速度,指定进度等
2,在自己的构造方法中获得这些属性
3,有些情况需要考虑重写onMeasure方法来确定控件大小,如使用wrap_content时
4,绘制出来自己需要的样子
今天我们首先来定义一个简单的控件,就像简易的TextView,参照上面的四个步骤我们可以开始代码之旅了
1, Textview需要什么属性,我擦类,肯定是要能显示字体,然后可以改变字体颜色,设定字体大小,还可以改变背景色,那么我们首先就在res/values下面新建一个attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="text" format="string" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<declare-styleable name="ViewText">
<attr name="text" />
<attr name="textColor" />
<attr name="textSize" />
</declare-styleable>
</resources>
上面我定义了三个属性,一个是要显示的text一个是textColor还有一个是textSize.
format是对应属性名的格式总共有:
1. reference:参考某一资源ID,以此类推
2. color:颜色值
3. boolean:布尔值
4. dimension:尺寸值。注意,这里如果是dp那就会做像素转换
5. float:浮点值。
6. integer:整型值。
7. string:字符串
8. fraction:百分数。
9. enum:枚举值
10. flag:是自己定义的,类似于 android:gravity="top",就是里面对应了自己的属性值。
接下来在我们布局文件中使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:wrh="http://schemas.android.com/apk/res/com.example.viewtext"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.viewtext.ViewText
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="106dp"
android:padding="10dp"
wrh:text="自定义Text"
wrh:textColor="#ff0000"
wrh:textSize="20sp" />
</RelativeLayout>
一定要在最外层布局中写 xmlns:wrh="http://schemas.android.com/apk/res/com.example.viewtext",后面com.xxxx是我们的包名
2,在我们的构造方法中获得这些属性
/**
* 需要显示的Text
*/
private String mText;
/**
* 字体颜色
*/
private int mTextColor;
/**
* 字体大小
*/
private int mTextSize;
/**
* 绘制区域
*/
private Rect mRect;
/**
* 画笔
*/
private Paint mPaint;
private final String TAG = "ViewText";
public ViewText(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
public ViewText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public ViewText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 获得我们在attrs里面声明的属性
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ViewText, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.ViewText_text:
mText = a.getString(attr);
break;
case R.styleable.ViewText_textColor:
// 默认为蓝色
mTextColor = a.getColor(attr, Color.BLUE);
break;
case R.styleable.ViewText_textSize:
// 字体大小
mTextSize = a.getDimensionPixelSize(attr, R.dimen.text_size);
break;
default:
break;
}
}
// 回收
a.recycle();
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mRect = new Rect();
// 获得字体需要多大地方显示
mPaint.getTextBounds(mText, 0, mText.length(), mRect);
}
3,需不需要重写onMeasure方法,首先我们需要了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
也就是说如果我们明确了大小的话是没有问题的,但是我们如果是把大小设为wrap_content,系统会自动把大小设为match_parent。所以这种情况我们还是重写onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获得宽度大小以及mode
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
// 获得长度大小以及mode
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 最终确定的大小
int width;
int height;
// 如果宽度是显示的指定了可以直接赋值
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
// 获得控件自适应的大小
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText, 0, mText.length(), mRect);
float textWidth = mRect.width();
int width2 = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = width2;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText, 0, mText.length(), mRect);
float textHeight = mRect.height();
int height2 = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = height2;
}
//设置控件大小
setMeasuredDimension(width, height);
}
4,最关键的绘制了,重写onDraw方法:
@Override
protected void onDraw(Canvas canvas) {
//默认背景为白色
mPaint.setColor(Color.WHITE);
//绘制显示区域
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
//字体颜色
mPaint.setColor(mTextColor);
//绘制字体居中
canvas.drawText(mText, getWidth() / 2 - mRect.width() / 2, getHeight()
/ 2 + mRect.height() / 2, mPaint);
}
效果也是很让人满意的,现在我们可以拓展一下,比如点击按钮字体变色?
public void setChangeColor(int color) {
mTextColor = color;
invalidate();
}
ViewText viewText;
Button button;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewText=(ViewText)findViewById(R.id.view1);
button=(Button)findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewText.setChangeColor(Color.BLUE);
}
});
}