前言
做程序开发,基础很重要。同样是拧螺丝人家拧出来的可以经久不坏,你拧出来的遇到点风浪就开始颤抖,可见基本功的重要性。此系列,专门收录一些看似基础,但是没那么简单的小细节,同时提供权威解决方案。喜欢的同志们点个赞就是对我最大的鼓励!先行谢过!
网上可能有一些其他文章,提供了解决方案,但是要么就是没有提供可运行demo
,要么就是demo不够纯粹
,让人探索起来受到其他代码因素的影响,无法专注于当前这个知识点(比如,我只是想了解Activity
的生命周期,你把生命周期探究的过程混入到一个很复杂的大杂烩Demo
中,让人一眼就没有了阅读Demo代码
的欲望),所以我觉得有必要做一个专题,用最纯粹
的方式展示一个坑
的解决方案.
干货
如下图所示,如果 你有一个自定义的TestView代码如下:只是简单的绘制一个文字
package study.hank.com.draw001.custom;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import study.hank.com.draw001.R;
import study.hank.com.draw001.Utils;
public class TestView extends View {
public TestView(Context context) {
this(context, null);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.aoyun_4));
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(Utils.dp2px(50));
canvas.drawText("测试文字", 100, 200, paint);
}
}
然而你不知道出于什么原因,将它放到了ScrollView
里面:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorF">
<study.hank.com.draw001.custom.TestView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
然后你运行起来,看到的:是这样的
哎呀?什么都没有?是的,什么都没有,但是如果我在 TextView里面加上onMeasure,变成下面这样
package study.hank.com.draw001.custom;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import study.hank.com.draw001.R;
import study.hank.com.draw001.Utils;
public class TestView extends View {
public TestView(Context context) {
this(context, null);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//比之前多出这个方法重写
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
setMeasuredDimension(widthSize, widthSize * 2);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.aoyun_4));
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(Utils.dp2px(50));
canvas.drawText("测试文字", 100, 200, paint);
}
}
再运行:就有文字显示了
原理
一般来说,很少有人会把自定义View放到ScrollView里面,但是如果发生了这种问题,我们应该要知道往哪个方向思考,所以探究一下原理,为什么我们自己对
UNSPECIFIED
进行处理之后,就能显示了呢?
进入ScrollView
的源码, 找到onMeasure
方法:
@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;
}
...省略一大段
}
}
我们可以看到ScrollView
对高的 UNSPECIFIED
并未做处理,直接return
了.
直接导致我们TestView
(如果TestView自己也不作处理的话
) 默认的测量方式得出的高度就是 初始值 0
,所以你什么都看不到.
所以,当我们自己给TestView
重写onMeasure
之后,
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
setMeasuredDimension(widthSize, 500);
Log.d("onMeasureTag", "第" + c++ + "次测量 - 之后的宽是:" +
getMeasuredWidth() + " / 高是:" + getMeasuredHeight());
}
}
就能看到文字了(为了表示TestView的范围,我加了个橙色背景)
这是此时的日志:
至于更深层次的原因,比如,为何ScrollView要对UNSPECIED不作处理。那就不得而知了,也没必要知道的这么清楚,谁知道谷歌大佬的想法呢···就这样了,欧了!