public class TextCustomView extends View { public TextCustomView(Context context) { super(context); init(context); } public TextCustomView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } private Paint paint; private void init(Context context) { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.parseColor("#00008a")); paint.setTextSize(BaseUtils.dip2px(context, 16)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.parseColor("#ffebcc"));//来个背景色方便看控件大小 String txt="换行必须用StaticLayout,drawText只显示一行,不信你看"; canvas.drawText(txt,10,400,paint); } }
放在LinearLayout中,显示没问题。
<?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" android:background="@color/white" android:orientation="vertical" tools:context=".InputActivity"> <com.lagou.test.view.input.TextCustomView android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
然后LinearLayout换成ScrollView之后 不显示了。
重写noMeasure方法:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); if (MeasureSpec.UNSPECIFIED == heightMode) { int heightSize = (int) (widthSize * 1.2f); setMeasuredDimension(widthMeasureSpec, heightSize); } }
这样就可以重新显示了。
原因是ScrollView的onMeasure并未处理MeasureSpec.UNSPECIFIED。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!mFillViewport) { return; } final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.UNSPECIFIED) { return; } ....... ....... }
父布局遇到MeasureSpec.UNSPECIFIED 直接return,导致子控件默认的测量方式得出的高度就是 初始值 0,所以不显示。此时需要子控件自己处理该case。
上面重写onMeasure可以优化为这样:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int reHeight = resolveSize(heightSize, heightMeasureSpec); setMeasuredDimension(widthMeasureSpec, reHeight); } }
resolveSize源码处理了 case MeasureSpec.UNSPECIFIED:
,源码如下
public static int resolveSize(int size, int measureSpec) { return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK; } public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { final int specMode = MeasureSpec.getMode(measureSpec); final int specSize = MeasureSpec.getSize(measureSpec); final int result; switch (specMode) { case MeasureSpec.AT_MOST: if (specSize < size) { result = specSize | MEASURED_STATE_TOO_SMALL; } else { result = size; } break; case MeasureSpec.EXACTLY: result = specSize; break; case MeasureSpec.UNSPECIFIED: default: result = size; } return result | (childMeasuredState & MEASURED_STATE_MASK); }