首先我们思考一个问题:
如果我们按照如下的方式自定义TextView,会有显示吗?
<?xml version="1.0" encoding="utf-8"?> <resources> <!--name 自定义View的名字 TextView--> <declare-styleable name="MyTextView"> <!-- name 属性名称 format 格式: string 文字 color 颜色 dimension 宽高 字体大小 integer 数字 reference 资源(drawable) --> <attr name="lzyText" format="string"/> <attr name="lzyTextColor" format="color"/> <attr name="lzyTextSize" format="dimension"/> <attr name="lzyMaxLength" format="integer"/> <!-- 枚举 --> <attr name="lzyInputType"> <enum name="number" value="1"/> <enum name="text" value="2"/> <enum name="password" value="3"/> </attr> </declare-styleable> </resources> public class MyTextView extends LinearLayout { private String mText; private int mTextColor; private int mTextSize; private Paint mPaint; //构造函数在代码new的时候调用 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); TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyTextView); mText = a.getString(R.styleable.MyTextView_lzyText); mTextColor = a.getColor(R.styleable.MyTextView_lzyTextColor, Color.BLACK); mTextSize = a.getDimensionPixelSize(R.styleable.MyTextView_lzyTextSize,20); a.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setTextSize(mTextSize); mPaint.setColor(mTextColor); } //布局的宽高都是有这个方法指定 @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); if (widthMode == MeasureSpec.AT_MOST){ Rect rect = new Rect(); mPaint.getTextBounds(mText,0,mText.length(),rect); width = rect.width(); } int height = MeasureSpec.getSize(heightMeasureSpec); if (heightMode == MeasureSpec.AT_MOST){ Rect r = new Rect(); mPaint.getTextBounds(mText,0,mText.length(),r); height = r.height(); } setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt(); int dy = 0; dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom; int baseLine = getHeight()/2 + dy; canvas.drawText(mText,0,baseLine,mPaint); } } <com.example.michael.view_01.MyTextView android:layout_width="wrap_content" android:layout_height="wrap_content" app:lzyText="hello world" app:lzyTextColor="@color/colorAccent" app:lzyTextSize="20sp" />当我们运行模拟器是,效果是这样的:
效果并没有出来,为什么我们自定义的TextView继承了一个ViewGroup效果就没有了呢?
draw(Canvas canvas)我们再View的源码里面看draw()方法:
if (!dirtyOpaque) onDraw(canvas);可见dirtyOpaque为false时,onDraw()方才会调用。
final int privateFlags = mPrivateFlags; final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;直接继承View会走onDraw()方法说明dirtyOpaque确实运算后会false。
下面我们来看ViewGroup的构造方法:
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initViewGroup(); initFromAttributes(context, attrs, defStyleAttr, defStyleRes); }进到initViewGroup()里面:这样dirtyOpaque会被重新赋值。
setFlags(WILL_NOT_DRAW, DRAW_MASK);