自定义控件–TextView 实现
1.创建attrs资源文件(main-res-values 下)-用于添加TextView的自定义属性
定义TextView自定义属性 format-属性类型 string-字符串 color-颜色 dimension-尺寸值
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TextView">
<attr name="text" format="string" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
</declare-styleable>
</resources>
1.新建TextView类
public class TextView extends View {
private String mText ;
private int mTextSize = 15;
private int mTextColor = Color.BLACK;
private Paint mPaint;
int width;
int height;
//new的时候调用
public TextView(Context context) {
this(context, null);
}
//在xml布局中使用
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
//xml文件中调用style中使用
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextView);
//获取文本
mText = array.getString(R.styleable.TextView_text);
//获取字体颜色
mTextColor = array.getColor(R.styleable.TextView_textColor, mTextColor);
//获取字体大小
mTextSize = array.getDimensionPixelSize(R.styleable.TextView_textSize, mTextSize);
//回收
array.recycle();
//实例化画笔
mPaint = new Paint();
//抗锯齿
mPaint.setAntiAlias(true);
//设置字体大小和颜色
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
}
}
重写onMeasure 测量控件大小
/**
* 自定义view的测量方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高的模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// MeasureSpec.AT_MOST 在布局种指定了wrap_content
// MeasureSpec.EXACTLY 在布局种制定了确定值 match_content
//MeasureSpec.UNSPECIFIED 尽可能的大 listview scrollview测量子布局的时候会用unspecified
if (widthMode == MeasureSpec.AT_MOST) {
//计算宽带
Rect bounds = new Rect();
//获取文本的Rect
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//文本宽度 = 外矩形高度+padd 左和右距离
width = bounds.width()+getPaddingLeft()+getPaddingRight();
}
if (heightMode == MeasureSpec.AT_MOST) {
//获取文本的Rect
Rect bounds = new Rect();
//计算宽带
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//文本高度 = 外矩形高度+padd 顶部和底部距离
height = bounds.height()+getPaddingTop()+getPaddingBottom();
}
//设置控件 宽 高
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//top bottom的值 是相对于基线来定义
Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
//fontMetricsInt.bottom,fontMetricsInt.top属性是指相对于基线的距离
int baseLine = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom + getHeight() / 2;
canvas.drawText(mText, getPaddingLeft(), baseLine, mPaint);
}
基线y点坐标获取,
获取文本框一半的高度,蓝色部分距离如下
(fontMetricsInt.bottom - fontMetricsInt.top) / 2-fontMetricsInt.bottom
完整的代码如下:
public class TextView extends View {
private String mText ;
private int mTextSize = 15;
private int mTextColor = Color.BLACK;
private Paint mPaint;
int width;
int height;
//new的时候调用
public TextView(Context context) {
this(context, null);
}
//在xml布局中使用
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
//xml文件中调用style中使用
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextView);
//获取文本
mText = array.getString(R.styleable.TextView_text);
//获取字体颜色
mTextColor = array.getColor(R.styleable.TextView_textColor, mTextColor);
//获取字体大小
mTextSize = array.getDimensionPixelSize(R.styleable.TextView_textSize, mTextSize);
//回收
array.recycle();
//实例化画笔
mPaint = new Paint();
//抗锯齿
mPaint.setAntiAlias(true);
//设置字体大小和颜色
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高的模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// MeasureSpec.AT_MOST 在布局种指定了wrap_content
// MeasureSpec.EXACTLY 在布局种制定了确定值 match_content
//MeasureSpec.UNSPECIFIED 尽可能的大 listview scrollview测量子布局的时候会用unspecified
if (widthMode == MeasureSpec.AT_MOST) {
//计算宽带
Rect bounds = new Rect();
//获取文本的Rect
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//文本宽度 = 外矩形高度+padd 左和右距离
width = bounds.width()+getPaddingLeft()+getPaddingRight();
}
if (heightMode == MeasureSpec.AT_MOST) {
//获取文本的Rect
Rect bounds = new Rect();
//计算宽带
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//文本高度 = 外矩形高度+padd 顶部和底部距离
height = bounds.height()+getPaddingTop()+getPaddingBottom();
}
//设置控件 宽 高
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//top bottom的值 是相对于基线来定义
Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
//fontMetricsInt.bottom,fontMetricsInt.top属性是指相对于基线的距离
int baseLine = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom + getHeight() / 2;
canvas.drawText(mText, getPaddingLeft(), baseLine, mPaint);
}
}
xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<comp.example.administrator.dragview.TextView
android:layout_width="260dp"
android:layout_height="40dp"
android:padding="10dp"
app:text="姓名"
app:textColor="#000000"
app:textSize="15sp" />
</LinearLayout>