自定义View之文字绘制相关问题。
1. 自定义属性
- 在values下面新建一个attrs的文件
<resources>
<!--name最好是自定义View的名字-->
<!--name:名称;format:格式-->
<!--字体大小,宽高 :dimension-->
<!--reference :资源的引用-->
<declare-styleable name="MyTextView">
</declare-styleable>
</resources>
在具有两个参数的构造函数中拿到TypedArray
,通过TypedArray
的get×××()
获取到自己在attrs
中定义的属性值。具体代码如下:
/*在布局中使用*/
public MyTextView(Context context, @NonNull AttributeSet attrs) {
this(context, attrs, 0);
TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyTextView);
mText=typedArray.getString(R.styleable.MyTextView_myText);
mTextColor=typedArray.getColor(R.styleable.MyTextView_myColor, mTextColor);
mTextSize=typedArray.getDimensionPixelSize(R.styleable.MyTextView_myTextSize, mTextSize);
/*回收*/
typedArray.recycle();
}
init():
public MyTextView(Context context, @Nullable AttributeSet attrs, int
defStyleAttr) {
super(context, attrs, defStyleAttr);
/*获取TypeArray*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
mText=typedArray.getString(R.styleable.MyTextView_myText);
mTextColor=typedArray.getColor(R.styleable.MyTextView_myColor, mTextColor);
mTextSize=SizeUtils.sp2px(context,typedArray.getDimensionPixelSize(R.styleable
.MyTextView_myTextSize,
mTextSize));
/*回收*/
typedArray.recycle();
init();
}
private void init() {
mTxtPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTxtPaint.setColor(mTextColor);
mTxtPaint.setTextSize(mTextSize);
mTxtRect=new Rect();
mTxtPaint.getTextBounds(mText,0,mText.length(),mTxtRect);
}
在编辑textView的时候,wrapcontent是通过字的个数来进行绘制的。
@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);
if (widthMode==MeasureSpec.AT_MOST){
widthSize=mTxtRect.width()+ getPaddingLeft() +getPaddingRight();
}
if (heightMode==MeasureSpec.AT_MOST){
heightSize=mTxtRect.height() + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(widthSize,heightSize);
}
注意如果在构造函数中使用的是this,那么所有的操作都是在第三个函数中进行操作:
/*在代码中声明*/
public MyTextView(Context context) {
this(context, null);
}
/*在布局中使用*/
public MyTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
/*使用style的时候会调用*/
public MyTextView(Context context, @Nullable AttributeSet attrs, int
defStyleAttr) {
super(context, attrs, defStyleAttr);
/*获取TypeArray*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
mText=typedArray.getString(R.styleable.MyTextView_myText);
mTextColor=typedArray.getColor(R.styleable.MyTextView_myColor, mTextColor);
mTextSize=typedArray.getDimensionPixelSize(R.styleable.MyTextView_myTextSize, mTextSize);
/*回收*/
typedArray.recycle();
init();
}
在使用canvas画字体的时候,坐标轴很重要,是以左下角的点进行的,也就是说x坐标是getWeidth()-mTxtRect.width(),y坐标是getHeight();
2. 文字的绘制
- textview有一个很重要的点是baseline。
//dy 代表的是:高度的一半到 baseLine的距离
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
// top 是一个负值 bottom 是一个正值 top,bttom的值代表是 bottom是baseLine到文字底部的距离(正值)
// 必须要清楚的,可以自己打印就好
int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int baseLine = getHeight()/2 + dy;
int x = getPaddingLeft();
canvas.drawText(mText,x,baseLine,mPaint);
- 其次是在
onMeasure
以及onDraw()
中注意padding值。 - 在自定义java中,单位统一是像素,所以在自定义属性中获取的单位需要进行转化如:dp2px,sp2px等等。
3. 相关问题
1. 如果自定义view继承自ViewGroup(Linearlayout,Relativelayout等)会怎样:
默认的viewGroup不会触发onDraw()的,所以使用onDraw的相关方法会失效。 模板模式
调用draw(Canvas canvas)