ScrollView 內(nèi)放置自定義View的坑

前言

做程序開(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í)知道谷歌大佬的想法呢···就這樣了,歐了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容