前言
做程序開(kāi)發(fā),基礎(chǔ)很重要。同樣是擰螺絲人家擰出來(lái)的可以經(jīng)久不壞,你擰出來(lái)的遇到點(diǎn)風(fēng)浪就開(kāi)始顫抖,可見(jiàn)基本功的重要性。此系列,專(zhuān)門(mén)收錄一些看似基礎(chǔ),但是沒(méi)那么簡(jiǎn)單的小細(xì)節(jié),同時(shí)提供權(quán)威解決方案。喜歡的同志們點(diǎn)個(gè)贊就是對(duì)我最大的鼓勵(lì)!先行謝過(guò)!
網(wǎng)上可能有一些其他文章,提供了解決方案,但是要么就是沒(méi)有提供可運(yùn)行demo,要么就是demo不夠純粹,讓人探索起來(lái)受到其他代碼因素的影響,無(wú)法專(zhuān)注于當(dāng)前這個(gè)知識(shí)點(diǎn)(比如,我只是想了解Activity的生命周期,你把生命周期探究的過(guò)程混入到一個(gè)很復(fù)雜的大雜燴Demo中,讓人一眼就沒(méi)有了閱讀Demo代碼的欲望),所以我覺(jué)得有必要做一個(gè)專(zhuān)題,用最純粹的方式展示一個(gè)坑的解決方案.
干貨
如下圖所示,如果 你有一個(gè)自定義的TestView代碼如下:只是簡(jiǎn)單的繪制一個(gè)文字
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("測(cè)試文字", 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>
然后你運(yùn)行起來(lái),看到的:是這樣的
image.png
哎呀?什么都沒(méi)有?是的,什么都沒(méi)有,但是如果我在 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);
}
//比之前多出這個(gè)方法重寫(xiě)
@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("測(cè)試文字", 100, 200, paint);
}
}
再運(yùn)行:就有文字顯示了
image.png
原理
一般來(lái)說(shuō),很少有人會(huì)把自定義View放到ScrollView里面,但是如果發(fā)生了這種問(wèn)題,我們應(yīng)該要知道往哪個(gè)方向思考,所以探究一下原理,為什么我們自己對(duì)
UNSPECIFIED進(jìn)行處理之后,就能顯示了呢?
進(jìn)入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對(duì)高的 UNSPECIFIED 并未做處理,直接return了.
直接導(dǎo)致我們TestView (如果TestView自己也不作處理的話) 默認(rèn)的測(cè)量方式得出的高度就是 初始值 0,所以你什么都看不到.
image.png
image.png
所以,當(dāng)我們自己給TestView重寫(xiě)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++ + "次測(cè)量 - 之后的寬是:" +
getMeasuredWidth() + " / 高是:" + getMeasuredHeight());
}
}
就能看到文字了(為了表示TestView的范圍,我加了個(gè)橙色背景)
image.png
這是此時(shí)的日志:
image.png
至于更深層次的原因,比如,為何ScrollView要對(duì)UNSPECIED不作處理。那就不得而知了,也沒(méi)必要知道的這么清楚,誰(shuí)知道谷歌大佬的想法呢···就這樣了,歐了!





