继承自View的文本显示控件CustomViewRandomInteger,需要自定义属性:字体颜色和字体大小。
第一步:资源文件中定义属性名称和属性类型
<span style="font-size:14px;"><span style="font-size:24px;"><resources>
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" />
<declare-styleable name="CustomViewRandomInteger">
<attr name="titleTextColor" />
<attr name="titleTextSize" />
</declare-styleable>
</resources></span></span>
第二步:在布局文件中添加CustomTextView:注意定义xmlns:custom,可以更改的是属性资源所在的包名[com.twelve],即manifest文件中定义的包名。
<span style="font-size:14px;"><span style="font-size:24px;"><LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.twelve"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.twelve.custom.CustomViewRandomInteger
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
custom:titleTextColor="#ff0000"
custom:titleTextSize="40sp" />
</LinearLayout></span></span>
第三步:获取自定义属性
<span style="font-size:14px;"><span style="font-size:24px;">/**
* 绘制时控制文本绘制的大小
*/
private Rect mBound;
/**
* 画笔
*/
private Paint mPaint;
public CustomViewRandomInteger(Context context) {
this(context, null);
}
public CustomViewRandomInteger(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 获得我自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomViewRandomInteger(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPaint = new Paint();
mBound = new Rect();
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.CustomViewRandomInteger, defStyle, 0);
int n = a.length();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
if (attr == R.styleable.CustomViewRandomInteger_titleTextColor){
// 默认颜色设置为黑色
mTitleTextColor = a.getColor(attr, Color.BLACK);
/**
* 获得绘制文本的颜色
*/
mPaint.setColor(mTitleTextColor);
}else if (attr == R.styleable.CustomViewRandomInteger_titleTextSize){
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = a.getDimensionPixelSize(attr,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,
getResources().getDisplayMetrics()));
/**
* 获得绘制文本的字号
*/
mPaint.setTextSize(mTitleTextSize);
}
}
a.recycle();
/**
* 获得绘制文本
*/
mTitleText = randomText();
/**
* 获取文本占据上控件画布的大小,定义点击事件
*/
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
this.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v)
{
mTitleText = randomText();
postInvalidate();
}
});
}
/**
* 产生一个有4个数字的字符串
*/
private String randomText()
{
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4)
{
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set)
{
sb.append("" + i);
}
return sb.toString();
}</span></span>
第四步:上面的三步获得了View类的属性:文字,文字大小,字体颜色。现在要考虑的是怎么把这些内容显示到手机屏幕上,产生视觉效果。
这里就引入了两个最重要的重载方法:onMeasure/onDraw。
-- 在进行之前要先说明一下,View在ViewGroup中的位置是由ViewGroup的,不是这里面的讨论返回,View自己能决定的是自己的大小和内部元素的大小和位置。
onMeasure的作用,是确定View对象的大小-- width/height。参数的说明见博客:自定义View之onMeasure() ,影响View大小的因素包括文字占用的面积textWidth/textHeight和文本距离边界的距离padding*。
<span style="font-size:14px;"><span style="font-size:24px;">@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
{
float textWidth = mBound.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else{
float textHeight = mBound.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}</span></span>
onDraw是在onMeasure的结果上,把视图输出到屏幕上,产生视觉效果。
<span style="font-size:14px;"><span style="font-size:24px;">@Override
protected void onDraw(Canvas canvas)
{
mPaint.setColor(Color.YELLOW);
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTitleTextColor);
if(mTitleText !=null){
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
}</span></span>
canvas参数是View要显示的位置,由ViewGroup传入,画布对象canvas的起始位置就是View对象在ViewGroup中的位置,canvas.drawRect前两个参数为零,画笔绘制的开始位置时画布对象canvas的左上角,沿着canvas的边界,绘制一个长方形。
canvas绘制文字,传入的坐标是文字左下角的位置。