Android控件架構(gòu)與自定義控件(二)

View的測(cè)量:

在現(xiàn)實(shí)生活中昔善,如果我們要去畫(huà)一個(gè)圖形,就必須知道他的大小和位置人柿。同樣Android系統(tǒng)在繪制View之前柴墩,也必須對(duì)View進(jìn)行測(cè)量,即告訴系統(tǒng)該畫(huà)一個(gè)多大的View凫岖,這個(gè)國(guó)政在onMeasure(0方法中進(jìn)行江咳。
Android系統(tǒng)給我們提供了一個(gè)設(shè)計(jì)短小精悍卻功能強(qiáng)大的類(lèi)------MeasureSpec類(lèi),通過(guò)他來(lái)幫助我們測(cè)量View隘截,MeasureSpec是一個(gè)32位的int值扎阶,其中高2位為測(cè)量的模式,低30位為測(cè)量的大小婶芭,在計(jì)算中使用位運(yùn)算的原因是為了提高并優(yōu)化效率。

測(cè)試的模式可以分為以下三種:
EXACTLY:
即精確值模式着饥,當(dāng)我們將控件的layout_width屬性或layout_height屬性指定為具體數(shù)值時(shí)犀农,比如android:layout_width="100dp",或者指定為match_parent屬性時(shí)(占據(jù)父View的大小)。系統(tǒng)使用的是EXACTLY模式宰掉。

AT_MOST:
即最大值模式呵哨,當(dāng)控件的layout_width屬性或layout_height屬性指定為wrap_content時(shí),控件大小一般隨著控件的子空間或內(nèi)容的變化而變化轨奄,此時(shí)控件的尺寸只要不超過(guò)父控件允許的最大尺寸即可孟害。

UNSPECIFIED:
這個(gè)屬性比較奇怪,挪拟,他不指定其大小測(cè)量模式挨务,View像多大就多大。通常情況下在繪制自定義View時(shí)才會(huì)使用。

View默認(rèn)的onMeasure(0方法只支持EXACTLY模式谎柄,所以如果在自定義控件的時(shí)候不重寫(xiě)onMeasure()方法丁侄,就只能使用EXACTLY模式〕祝控件可以響應(yīng)你指定的具體寬高值或者是match_warp屬性鸿摇,而如果要讓自定義View支持wrap_content屬性,那么就必須重寫(xiě)onMeasure(0方法來(lái)指定wrap_content時(shí)的大小劈猿。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

通過(guò)查看源碼拙吉,可以發(fā)現(xiàn)其最終調(diào)用的是:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

setMeasuredDimension這個(gè)方法。該方法試將測(cè)量后的寬高設(shè)置進(jìn)去揪荣。從而完成測(cè)量工作筷黔。所以我們就可以重寫(xiě)onMeasure()方法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

我們調(diào)用自定義的measureWidth()和measureHeight()方法分別對(duì)寬高進(jìn)行重新定義。

private int measureWidth(int measureSpec) {
        // TODO Auto-generated method stub
        int result=0;
        int specMode=MeasureSpec.getMode(measureSpec);
        int specSize=MeasureSpec.getSize(measureSpec);
        
        if(specMode==MeasureSpec.EXACTLY){
            result=specSize;
        }else{
            result=200;
            if(specMode==MeasureSpec.AT_MOST){
                result=Math.min(result, specSize);
            }
        }
        
        
        return result;
    }

上面的方法是通過(guò)判斷測(cè)量的模式变逃,給出不同的測(cè)量值必逆,當(dāng)specMode為EXACTLY時(shí),直接使用指定的specSize即可揽乱,當(dāng)specMode為其他兩種模式石名眉,需要給他一個(gè)默認(rèn)的大小。特別的如果制定wrap_content屬性凰棉,即AT_MOST模式损拢,則需要取出我們指定的大小與soecSize中的最小的一個(gè)來(lái)作為最后的測(cè)量值。上面的代碼基本上可以作為模板代碼撒犀。

運(yùn)行上面代碼福压,當(dāng)指定寬高為400PX的時(shí)候就會(huì)顯示具體的寬高View。如果制定為match_parent屬性時(shí)或舞,就會(huì)match父view荆姆,當(dāng)指定寬高屬性為wrap_content的時(shí)候,如果不重寫(xiě)onMeasure()方法映凳,那么系統(tǒng)就不知道該使用默認(rèn)多大的尺寸胆筒,因此,他就會(huì)默認(rèn)填充整個(gè)父布局诈豌,所以重寫(xiě)仆救,onMeasure()方法的目的,就是為了能夠給View一個(gè)wrap_conteng屬性下的默認(rèn)大小矫渔。(感覺(jué)說(shuō)了這么多還是這句最明白彤蔽。擦。庙洼。顿痪。镊辕。)

View的繪制:

onDraw(),Canvas ,Paint這三個(gè)就是繪制所必需的具體的我就不多說(shuō)了。

如果是在onDraw方法里面就不用創(chuàng)建Canvas對(duì)象员魏,因?yàn)閰?shù)里面就有丑蛤,那么如果在別的地方就需要new出來(lái)一個(gè):

Canvas camvas=new Canvas(bitmap);

為什么要穿進(jìn)去一個(gè)bitmap對(duì)象?如果不傳入一個(gè)bitmap撕阎,IDE編譯雖然不報(bào)錯(cuò)受裹,但是一般我們不會(huì)這樣做。這是因?yàn)榇┻M(jìn)去的bitmap與通過(guò)這個(gè)bitmap穿件的Canvas畫(huà)布是僅僅聯(lián)系在一起的虏束。這個(gè)過(guò)程我們稱(chēng)之為裝載畫(huà)布棉饶。這個(gè)bitmap是用來(lái)存儲(chǔ)所有繪制在Canvas上的像素信息。所以當(dāng)你通過(guò)這種方式創(chuàng)建了Canvas對(duì)戲那個(gè)后镇匀,門(mén)后面調(diào)用所有的Canvas.drawXXX方法都是發(fā)生在這個(gè)bittmap上照藻。如果在View類(lèi)的onDraw方法中,通過(guò)下面這段代碼汗侵,我們可以了解到bitmap與canvas直接的關(guān)系幸缕。首先在onDraw方法中繪制兩個(gè)bitmap:

@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        canvas.drawBitmap(bitmap1, 0, 0,null);
        canvas.drawBitmap(bitmap2, 0, 0,null);
    }

而對(duì)于bitmap2我們將他裝載到另一個(gè)Canvas對(duì)象中:

Canvas mCanvas=new Canvas(bitmap2);

在其他地方使用Canvas對(duì)象的繪圖方法在裝載bitmap2的Canvas對(duì)象上進(jìn)行繪圖:

mCamvas.drawXXX

通過(guò)mCanvas將繪制效果作用在了bittmap2上,再刷新View的時(shí)候就會(huì)發(fā)現(xiàn)通過(guò)onDraw方法畫(huà)出來(lái)的bitmap2已經(jīng)發(fā)生了改變晰韵,這就是因?yàn)閎itmap2承載了在mCanvas上所進(jìn)行的繪圖操作发乔。雖然我們也使用了Canvas的繪制API。但其實(shí)并沒(méi)有經(jīng)圖形直接繪制在onDraw()方法指定的那塊畫(huà)布上雪猪,而是通過(guò)改變bitmap栏尚,然后讓View重回,從而顯示改變之后的bittmap只恨。

ViewGroup的測(cè)量:

ViewGroup會(huì)去管理其子View译仗,其中的一個(gè)管理項(xiàng)目就是負(fù)責(zé)子View的顯示大小,當(dāng)Vie我Group的大小為wrap_content時(shí)官觅,ViewGroup就需要對(duì)子View進(jìn)行遍歷纵菌,以便獲得所有子view的大學(xué)哎奧,從而來(lái)決定自己的大小休涤,而在其他模式下則會(huì)通過(guò)具體的指定值來(lái)設(shè)置自身的大小产艾。

ViewGroup在測(cè)量時(shí)通過(guò)遍歷所有子View從而調(diào)用子View 的Measure方法獲得每一個(gè)子View的測(cè)量結(jié)果前面所說(shuō)的對(duì)View的測(cè)量就是在這里進(jìn)行的。
當(dāng)子View測(cè)量完畢后滑绒,就需要將子View放到合適的位置,這個(gè)過(guò)程就是View的Layout過(guò)程隘膘,ViewGroup在執(zhí)行Layout過(guò)程時(shí)疑故,同樣是使用遍歷來(lái)調(diào)用子View的Layout方法,并指定其具體的顯示位置弯菊,從而來(lái)決定其布局的位置纵势。

在自定義ViewGroup時(shí),通常會(huì)去重寫(xiě)onLayout()方法來(lái)控制其子View顯示位置的邏輯。同樣钦铁,如果需要支持wrap_content屬性软舌,那么他還必須重寫(xiě)onMeasure()方法,這點(diǎn)與VIEW是相同的牛曹。

ViewGroup的繪制:

ViewGroup通常情況下不需要繪佛点,因?yàn)樗旧砭蜎](méi)有需要繪制的東西,如果不是指定了ViewGroup的北京顏色黎比,那么ViewGroup的onDraw()方法都不會(huì)被調(diào)用超营。但是,ViewGRoup會(huì)使用dispatchDraw()方法來(lái)繪制其子View阅虫,其過(guò)程同樣是遍歷所有的子View演闭,并調(diào)用子View的繪制方法來(lái)完成繪制工作的。

自定義View:

在View中通常有以下一些比較重要的回調(diào)方法:

onFinishInflate():從XML加載組件后回調(diào)颓帝。

onSizeChanged():組件大小改變時(shí)回調(diào)米碰。

onMeasure():回調(diào)給方法來(lái)進(jìn)行測(cè)量。

onLayout():回調(diào)該方法來(lái)確定顯示的位置购城。

onTouchEvent():監(jiān)聽(tīng)到觸摸事件時(shí)的回調(diào)吕座。

通常情況下,由以下三種方法來(lái)實(shí)現(xiàn)自定義控件:

對(duì)現(xiàn)有控件進(jìn)行拓展

通過(guò)組合來(lái)實(shí)現(xiàn)新的控件

重寫(xiě)View來(lái)實(shí)現(xiàn)全新的控件

對(duì)現(xiàn)有控件進(jìn)行拓展:

以一個(gè)TextView為例工猜,比如米诉,讓一個(gè)TextView的背景更加豐富,給其多繪制幾層背景篷帅。

我們先來(lái)分析一下如何實(shí)現(xiàn)史侣,原生的TextView使用onDraw()方法繪制要顯示的文字,當(dāng)繼承了系統(tǒng)deTextView之后魏身,如果不重寫(xiě)其onDraw()方法惊橱,則不會(huì)修改TextView的任何效果,可以認(rèn)為在自定義的TextView中調(diào)用TextView類(lèi)的onDraw()方法來(lái)繪制了顯示的文字箭昵,代碼如下:

@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
    }

程序調(diào)用super.onDraw(canvas)方法來(lái)實(shí)現(xiàn)原生控件的功能税朴,但是在調(diào)用super.onDraw()方法之前和之后,我們都可以實(shí)現(xiàn)自己的邏輯家制,分別在系統(tǒng)繪制文字前后正林,完成自己的操作,即如下:

@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        //在回調(diào)父類(lèi)方法前颤殴,實(shí)現(xiàn)自己的邏輯觅廓,對(duì)TextView來(lái)說(shuō)即是在繪制文本內(nèi)容前
        super.onDraw(canvas);
        //在回調(diào)父類(lèi)方法后,實(shí)現(xiàn)自己的邏輯涵但。對(duì)TextVeiw來(lái)說(shuō)既是在繪制文本內(nèi)容后
    }

這樣就可以改變?cè)到y(tǒng)繪制的方式杈绸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帖蔓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞳脓,更是在濱河造成了極大的恐慌塑娇,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劫侧,死亡現(xiàn)場(chǎng)離奇詭異埋酬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)板辽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)奇瘦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人劲弦,你說(shuō)我怎么就攤上這事耳标。” “怎么了邑跪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵次坡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我画畅,道長(zhǎng)砸琅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任轴踱,我火速辦了婚禮症脂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淫僻。我一直安慰自己诱篷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布雳灵。 她就那樣靜靜地躺著棕所,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悯辙。 梳的紋絲不亂的頭發(fā)上琳省,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音躲撰,去河邊找鬼针贬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拢蛋,可吹牛的內(nèi)容都是我干的坚踩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瓤狐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞬铸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起础锐,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嗓节,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后皆警,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拦宣,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年信姓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸵隧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡意推,死狀恐怖豆瘫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菊值,我是刑警寧澤外驱,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站腻窒,受9級(jí)特大地震影響昵宇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜儿子,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一瓦哎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柔逼,春花似錦蒋譬、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至儡毕,卻和暖如春也切,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腰湾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工雷恃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人费坊。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓倒槐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親附井。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讨越,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容