一.基础知识:
在进入自定义View的学习前,先了解相关的知识点才能更好帮助我们去理解和掌握。自定义View可以通过不同的实现方法进行分类,有继承View的,也有继承ViewGroup的。常见控件如:Textview也属于自定义View的范畴,在后面我们会去动手实现一个TextView。
开始学习前,我们先从继承View的实现方法入手,View的绘制流程包括了以下三部分:
onMeasure:测量。包括三种模式:unSpecified,exactly,at_most。具体不同在后面再详细分析
onDraw:绘制。Canvas画布绘制效果
onTouch:触摸。涉及到不同的MotionEvent和事件分发
在自定义View中,还包括最重要的自定义属性,这部分用于配置自定义属性,如颜色,字符文本,尺寸等。能够使得自定义View充满灵活性和多样性。
效果图:
二.自定义属性:
自定义属性的一般格式,在values目录下新建一个attrs文件:
这里的CiuTextColor,CiuText和CiuTextSize就是在布局文件中引用的命名,后面的format则是自定义属性的类型。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CiuTextView">
<attr name="CiuTextColor" format="color" />
<attr name="CiuText" format="string" />
<attr name="CiuTextSize" format="dimension" />
</declare-styleable>
</resources>
Layout布局中使用:
<com.demo.customizeview.CiuTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:CiuText="我的自定义CiuTextview"
app:CiuTextColor="@color/colorAccent"
app:CiuTextSize="16dp" />
小Tips:如果设置完所有东西但是在Android Studio中没有预览效果,可以clean+rebuild一下,即可看到预览效果啦。
三.完整代码:
public class CiuTextView extends View {
//设置自定义属性,Panit为画笔
private String mText;
private int mTextColor;
private int mTextSize;
private Paint mPaint;
//每个自定义View都要实现以下几个构造方法,我们一般在第三个进行初始化工作。
public CiuTextView(Context context) {
this(context,null);
}
public CiuTextView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
//获取attrs文件下的自定义属性
public CiuTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context,attrs,defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CiuTextView);
mText = typedArray.getString(R.styleable.CiuTextView_CiuText);
mTextColor = typedArray.getColor(R.styleable.CiuTextView_CiuTextColor,mTextColor);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.CiuTextView_CiuTextSize,mTextSize);
typedArray.recycle();
mPaint = new Paint();
//设置抗锯齿
mPaint.setAntiAlias(true);
//设置字体颜色
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
}
//自定义view的测量
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
//获取自定义View 的宽高模式,即上面提到的onMeasure的三种模式。
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//如果自定义TextView的宽高是确定的话,如16dp,match_parent等则不用计算
//如果自定义TextView的宽高给的是wrap_content,则需要计算
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//计算宽度
if(widthMode == MeasureSpec.AT_MOST){
@SuppressLint("DrawAllocation")
//获取自定义View的矩形区域
Rect bounds = new Rect();
//getTextBounds的参数分别表示:要测量的文字,开始位置,结束位置,以及控件矩形区域
mPaint.getTextBounds(mText,0,mText.length(),bounds);
width = bounds.width()+getPaddingLeft()+getPaddingRight();
}
//计算高度
if(heightMode == MeasureSpec.AT_MOST){
@SuppressLint("DrawAllocation")
Rect bounds = new Rect();
mPaint.getTextBounds(mText,0,mText.length(),bounds);
height = bounds.height()+getPaddingTop()+getPaddingBottom();
}
//设置控件宽高
setMeasuredDimension(width,height);
}
//正式绘制
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//计算基线
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int baselint = getHeight()/2+dy;
//drawText参数分别为:文本,文本在屏幕左边的位置,基线baseline在屏幕的位置,画笔
canvas.drawText(mText,getPaddingLeft(),baselint,mPaint);
}
}