ScrollView中自定义View不显示

阳光穿透心脏的1/2处 2023-01-17 13:53 177阅读 0赞
  1. public class TextCustomView extends View {
  2. public TextCustomView(Context context) {
  3. super(context);
  4. init(context);
  5. }
  6. public TextCustomView(Context context, @Nullable AttributeSet attrs) {
  7. super(context, attrs);
  8. init(context);
  9. }
  10. private Paint paint;
  11. private void init(Context context) {
  12. paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  13. paint.setColor(Color.parseColor("#00008a"));
  14. paint.setTextSize(BaseUtils.dip2px(context, 16));
  15. }
  16. @Override
  17. protected void onDraw(Canvas canvas) {
  18. super.onDraw(canvas);
  19. canvas.drawColor(Color.parseColor("#ffebcc"));//来个背景色方便看控件大小
  20. String txt="换行必须用StaticLayout,drawText只显示一行,不信你看";
  21. canvas.drawText(txt,10,400,paint);
  22. }
  23. }

放在LinearLayout中,显示没问题。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <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">
  3. <com.lagou.test.view.input.TextCustomView android:layout_width="match_parent" android:layout_height="wrap_content" />
  4. </LinearLayout>

在这里插入图片描述

然后LinearLayout换成ScrollView之后 不显示了。

重写noMeasure方法:

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  4. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  5. int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  6. if (MeasureSpec.UNSPECIFIED == heightMode) {
  7. int heightSize = (int) (widthSize * 1.2f);
  8. setMeasuredDimension(widthMeasureSpec, heightSize);
  9. }
  10. }

这样就可以重新显示了。
原因是ScrollView的onMeasure并未处理MeasureSpec.UNSPECIFIED。

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  4. if (!mFillViewport) {
  5. return;
  6. }
  7. final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  8. if (heightMode == MeasureSpec.UNSPECIFIED) {
  9. return;
  10. }
  11. .......
  12. .......
  13. }

父布局遇到MeasureSpec.UNSPECIFIED 直接return,导致子控件默认的测量方式得出的高度就是 初始值 0,所以不显示。此时需要子控件自己处理该case。

上面重写onMeasure可以优化为这样:

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  4. int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  5. int reHeight = resolveSize(heightSize, heightMeasureSpec);
  6. setMeasuredDimension(widthMeasureSpec, reHeight);
  7. }
  8. }

resolveSize源码处理了 case MeasureSpec.UNSPECIFIED: ,源码如下

  1. public static int resolveSize(int size, int measureSpec) {
  2. return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
  3. }
  4. public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
  5. final int specMode = MeasureSpec.getMode(measureSpec);
  6. final int specSize = MeasureSpec.getSize(measureSpec);
  7. final int result;
  8. switch (specMode) {
  9. case MeasureSpec.AT_MOST:
  10. if (specSize < size) {
  11. result = specSize | MEASURED_STATE_TOO_SMALL;
  12. } else {
  13. result = size;
  14. }
  15. break;
  16. case MeasureSpec.EXACTLY:
  17. result = specSize;
  18. break;
  19. case MeasureSpec.UNSPECIFIED:
  20. default:
  21. result = size;
  22. }
  23. return result | (childMeasuredState & MEASURED_STATE_MASK);
  24. }

发表评论

表情:
评论列表 (有 0 条评论,177人围观)

还没有评论,来说两句吧...

相关阅读

    相关 定义view使用selector

    需求如下:整体是一个自定义view,该view中有一个图片,点击后切换状态,图片跟着切换。 很自然的想到selector中的state切换,定义一个selector文件如下

    相关 Android定义View

    如何自定义控件 1. 自定义属性的声明和获取 2. 测量onMeasure:测量自定义控件的尺寸 3. 绘制onDraw:绘制自定义控件 4. 状态的存储与恢复: