讓自定義 View 支持 ScrollView

看過《Android 開發(fā)藝術探索》一書的小伙伴都知道,這本書將自定義 View 分成四個類型,分別是:

  • 繼承 View 重寫 onDraw 方法
  • 繼承 ViewGroup 派生特殊的 Layout
  • 繼承已有的 View
  • 繼承已有的 ViewGroup

我們本次并不討論具體的類型應該如何實現(xiàn)焚辅,自定義 View 的范圍實在是太寬廣了,只有想不到,沒有做不到。在書中任玉剛大大還提到了自定義 View 應該注意的幾個方面:

  • 讓 View 支持 wrap_content
  • 讓 View 支持 padding
  • 盡量不要在 View 中使用 Handler
  • View 中有線程或者動畫豪筝,需要及時停止
  • View 有滑動嵌套情形的,需要處理好滑動沖突

這些注意事項都非常有用摘能,即使是一個新手做自定義 View续崖,在本書的指引下,遵循這些標準也能做出可用性較高的自定義 View团搞,比如說我(微笑)严望。不過我在實踐的過程中發(fā)現(xiàn)一個任玉剛大大沒有提到的方面,那就是讓自定義 View 支持 ScrollView莺丑,畢竟 ScrollView 已經(jīng)是個非常常用的布局了著蟹。

首先看一個小例子,我們就拿書中的自定義 View 案例來示范梢莽,也就是單純的畫個圓:

public class CircleView extends View {
    private int mColor = getResources().getColor(R.color.colorAccent);
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        init();
    }

    public CircleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
        mColor = array.getColor(R.styleable.CircleView_circle_color,
                getResources().getColor(R.color.colorAccent));
        array.recycle();
        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 200);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint);
    }
}

以上代碼除了顏色我沒有做其他改動,將這個 CircleView 放在縱向的 LinearLayout 中奸披,寬度設置 match_parent昏名,高度設置 wrap_content,背景色設置為黑色阵面,為了比較轻局,在其下面放一個 TextView,我們來看看顯示的結果:

還是很正常的样刷,符合我們的預期仑扑。

如果在 Layout 最外層套一個 ScrollView,再來看看:

自定義 View 看不見了置鼻!首先自定義 View 的外層是 LinearLayout镇饮,高度是 match_parent,從常理來分析箕母,ScrollView 內部的高度無限大的储藐,如果內部的 View 的不做精確設置俱济,可能會導致 View 無限大,所以 ScrollView 內部沒有設置精確高度的 View 都會無法顯示钙勃,除非內部做特殊處理蛛碌。比如下面的 TextView ,設置的高度也是 wrap_content辖源,但它卻能顯示蔚携,為什么呢?按照我們在 onMeasure 方法中的邏輯克饶,如果自定義 View 是大小不定酝蜒,也就是對應 MeasureSpec.AT_MOST,那么寬高都應該為默認的 200 才對彤路,這樣也不會不顯示秕硝。那么就調試一下看看:

heightMeasureSpec 的值是0,我們知道 MesureSpec 是一個 32 位的 int 值洲尊,高 2 位表示測量模式远豺,低 30 位表示在這種模式下的測量值。顯然這不屬于任何一種 MeasureSpec 已知的模式坞嘀,所以自定義 View 無法獲得測量高度躯护,也就無法顯示了。知道了原因就好辦了丽涩,只需要對 heightMeasureSpec 的值作出識別處理就行了棺滞,比如下面是我的方法:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        //避免在 scrollView 里獲取不到高度
        if (heightMeasureSpec == 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.AT_MOST);
        }
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, widthSpecSize);
        }
    }

如果無法獲取 heightMeasureSpec,就用 widthSpecSize 重新實例化一個 heightMeasureSpec 出來矢渊,模式設置為 AT_MOST继准,值默認與寬度相同,如果獲取不到高度矮男,就默認設置為與寬度相同移必。因為這里是個圓,那么就有個好處毡鉴,即使寬高設置的都是 match_parent崔泵,那么真正的高度也只是最大寬大的值,畢竟在 ScrollView 中高度是不會有 match_parent 的效果的猪瞬。當然根據(jù)自己的 View 的用途最好設置適合的默認值憎瘸。

看看效果:

再把高度設置為 match_parent

一樣的效果,這種在 ScrollView 中就算是一種比較合理的方式陈瘦,并且完全不會影響自定義 View 在非 ScrollView 布局中的表現(xiàn)幌甘。所以除了任玉剛大大提到的 5 點注意事項,我還想再加一條,那就是 讓自定義 View 支持 ScrollView含潘。

本文最早發(fā)布于 alphagao.com 饲做。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市遏弱,隨后出現(xiàn)的幾起案子盆均,更是在濱河造成了極大的恐慌,老刑警劉巖漱逸,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泪姨,死亡現(xiàn)場離奇詭異,居然都是意外死亡饰抒,警方通過查閱死者的電腦和手機肮砾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袋坑,“玉大人仗处,你說我怎么就攤上這事≡婀” “怎么了婆誓?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長也颤。 經(jīng)常有香客問我洋幻,道長,這世上最難降的妖魔是什么翅娶? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任文留,我火速辦了婚禮,結果婚禮上竭沫,老公的妹妹穿的比我還像新娘燥翅。我一直安慰自己,他們只是感情好蜕提,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布权旷。 她就那樣靜靜地躺著,像睡著了一般贯溅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躲查,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天它浅,我揣著相機與錄音,去河邊找鬼镣煮。 笑死姐霍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播镊折,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼胯府,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了恨胚?” 一聲冷哼從身側響起骂因,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赃泡,沒想到半個月后寒波,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡升熊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年俄烁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片级野。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡页屠,死狀恐怖,靈堂內的尸體忽然破棺而出蓖柔,到底是詐尸還是另有隱情辰企,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布渊抽,位于F島的核電站蟆豫,受9級特大地震影響,放射性物質發(fā)生泄漏懒闷。R本人自食惡果不足惜十减,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愤估。 院中可真熱鬧帮辟,春花似錦、人聲如沸玩焰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昔园。三九已至蔓榄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間默刚,已是汗流浹背甥郑。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荤西,地道東北人澜搅。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓伍俘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勉躺。 傳聞我的和親對象是個殘疾皇子癌瘾,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內容