android之view的繪制基礎(chǔ)

其實本來想看自定義view的,但是看到郭神的view的繪制骡尽,感覺還是有必要看的遣妥,本人腦子不好使擅编,看完會忘記攀细,于是自己來做筆記,作為一個合格的android程序員還是有必要對基礎(chǔ)知識的鞏固爱态,做到學而時習之谭贪。

感謝郭神慷慨解囊的blog,支持原創(chuàng)锦担,經(jīng)典解析看這里:http://blog.csdn.net/guolin_blog/article/details/16330267

1俭识、LayoutInflater分析

首先看下layoutInflater用法:
用法一:
LayoutInflater inflater = LayoutInflater.from(context);
用法二:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

然后用法就是這樣了

 inflater.inflate(int resource, ViewGroup root, boolean attachToRoot);

參數(shù)一:是要加載的布局ID;
參數(shù)二:是給該布局的外部嵌套一層父布局
參數(shù)三:需要根據(jù)第二個參數(shù)來判斷

  • 1洞渔、 如果root為null, attachToRoot將失去作用套媚, 設(shè)置任何值都沒有意義
  • 2、如果root不為null, attachToRoot設(shè)為true,則會給加載的布局文件制定一個父布局磁椒,也就是root
  • 3堤瘤、如果root不為null, attachToRoot設(shè)為false浆熔, 則會在布局文件最外層的所有l(wèi)ayout屬性進行設(shè)置本辐,當該view被添加到父view中時, 那些layout會自動生效。
  • 4慎皱、在不設(shè)置attachToRoot參數(shù)下老虫,如果root不為null,attachToRoot默認為true

2茫多、如果設(shè)置一個布局

<Button xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="300dp"  
    android:layout_height="80dp"  
    android:text="Button" >  
 
</Button>

然后在代碼中用add的方式添加代碼

 @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mainLayout = (LinearLayout) findViewById(R.id.main_layout);  
        LayoutInflater layoutInflater = LayoutInflater.from(this);  
        View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);  
        mainLayout.addView(buttonLayout);  
    }  

在xml布局中給button設(shè)置寬高發(fā)現(xiàn)沒有任何變化祈匙,這是因為layout_width和layout_height是設(shè)置View在布局中的大小,也就是首先view必須存在于一個布局中天揖,

那么在setContentView中設(shè)置就可以呢菊卷?
@Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mainLayout = (LinearLayout) findViewById(R.id.main_layout);  
        ViewParent viewParent = mainLayout.getParent();  
        Log.d("TAG", "the parent of mainLayout is " + viewParent);  
    }  

從上面的代碼中可以看出,打印的結(jié)果是FrameLayout布局宝剖,在activity中setContentView的時候會給我們的布局最外層加上一個FrameLayout洁闰,于是layout_width和layout_height屬性就有效果。

3万细、activity的組成:

activity由標題和FrameLayout布局組成扑眉,標題可以通過代碼的方式隱藏

requestWindowFeature(Window.FEATURE_NO_TITLE);

上述代碼需要在setContentView之前設(shè)置

標題下面就是一個FrameLayout,activity中的xml布局就是通過setContentView掛在布局到FrameLayout中赖钞,F(xiàn)rameLayout的布局id是content

android:id="@+id/content"

4腰素、onMeasure()

onMeasure()用于測量視圖的大小,view的繪制流程是從ViewRoot的performTraversals()方法中開始的雪营,內(nèi)部調(diào)用的是measure(widthMeasureSpec, heightMeasureSpec);方法弓千,widthMeasureSpec是由父控件計算后傳遞給子控件的,也就是說一定程度上父控件決定了子控件的大小献起。

1洋访、MeasureSpec是有specSize和specMode共同組成的,specSize記錄的是大小谴餐,specMode記錄的是規(guī)格姻政,模式
  • 1、EXACTLY(精確的)
    表示父視圖希望子視圖的大小應該是由是specSize的值決定岂嗓,系統(tǒng)默認會按照這個規(guī)則來設(shè)置子視圖大小汁展,也可以自己設(shè)置固定的值。
  • 2厌殉、AT_MOST(至多)
    表示子視圖最多只能是specSize指定大大小础拨,盡可能小的設(shè)置這個視圖技肩,并且保證不超過specSize的大小。
  • 3、UNSPECIFIDE(不確定螃概,未指定)
    父控件不對子元素施加任何的束縛佑菩,可以按照自己的意愿設(shè)置成任意大小秘狞,沒有任何限制。

MeasureSpec是由mode+size組合而成的树姨。

(2)、模式的提取

我們知道widthMeasureSpec和heightMeasureSpec是有模式和數(shù)值組成的桥状,二進制去前兩位代表模式帽揪,后28位代表數(shù)字。android中用MeasureSpec為我們提供了取模式和數(shù)值的類和方法辅斟。

//獲取模式mode
MeasureSpec.getMode(int spec);   
//獲取數(shù)值
MeasureSpec.getSize(int spec);
(3)转晰、模式的由來

模式的由來來自于XML的定義。相對于簡單的來說士飒,xml布局和模式的對應關(guān)系

wrap_content  ------->MeasureSpec.AT_MOST
match_parent -------->MeausreSpec.EXACTLY
20dp(具體值) --------->MeasureSpec.EXACTLY

一定要注意是查邢,當模式是MeasureSpec.EXACTLY時,我們就不必要設(shè)定我們計算的大小了酵幕,因為這個大小是用戶指定的扰藕,我們不應更改。但當模式是MeasureSpec.AT_MOST時芳撒,也就是說用戶將布局設(shè)置成了wrap_content邓深,我們就需要將大小設(shè)定為我們計算的數(shù)值,因為用戶根本沒有設(shè)置具體值是多少笔刹,需要我們自己計算芥备。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int width_mode = MeasureSpec.getMode(widthMeasureSpec);
        int width_size = MeasureSpec.getSize(widthMeasureSpec);

        int height_mode = MeasureSpec.getMode(heightMeasureSpec);
        int height_size = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension((width_mode == MeasureSpec.EXACTLY)? width_size : width, (height_mode == MeasureSpec.EXACTLY)? height_size: height);

需要注意的是,在setMeasuredDimension()方法調(diào)用之后舌菜,我們才能使用getMeasuredWidth()和getMeasuredHeight()來獲取視圖測量出的寬高萌壳,以此之前調(diào)用這兩個方法得到的值都會是0。

OnLayout();

測量完控件之后就是如何擺放的問題了日月。
先來看看方法:

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
      layout(left, top, right, bottom);
    }

進入到父類袱瓮,居然是一個空方法,在進入到layout()方法中山孔,layout接收四個參數(shù)懂讯,分別是左荷憋、上台颠、右、下勒庄。layout會先調(diào)用setFrame()方法來判斷視圖是否發(fā)生改變串前,來確定有沒有必要重繪視圖,接著就是走onLayout()实蔽,這個方法剛才看了荡碾,是一個空方法,因為這個是由布局去實現(xiàn)的局装,父控件決定子控件的顯示位置坛吁。

在到viewGroup的onLayout方法中劳殖,發(fā)現(xiàn)是一個抽象方法,那么當我們自定義viewGroup的時候就必須去實現(xiàn)onLayout()這個方法拨脉,

getWidth()和getMeasureWidth();的區(qū)別

其實這兩個值基本上是相同的哆姻,

  • 1、getMeasureWidth()方法在measure()過程結(jié)束后獲取到了玫膀,而getWidth()則需要在Layout()之后才能獲取到
  • 2矛缨、getMeasureWidth()是通過setMeasureDimension()方法來進行設(shè)置的,getWidth()是通過控件最右邊的減去控件最左邊的值得到的帖旨。

一個getMeasureWidth是measure后的一個系統(tǒng)建議值箕昭,但是我們在layout()的時候確實可以寫自己的值,通常情況下是不推薦這么寫的解阅。所以就導致通常情況下getMeasurewidht和getWidth的值是一致的落竹。

OnDraw()

測量完成以后就開始繪制過程了。
1货抄、首先是對背景的繪制筋量,根據(jù)layout過程來確定視圖位置來設(shè)置背景的繪制區(qū)域, 之后調(diào)用drawable的draw繪制完成碉熄。
2.對視圖內(nèi)容進行繪制桨武,可以看到ondraw()空的,因為每個視圖的內(nèi)容部分肯定都是各不相同的锈津,這部分的功能交給子類來去實現(xiàn)也是理所當然的呀酸。
3、繪制滾動條琼梆。任何一個控件都有滾動條的性誉,你看不到只是隱藏了而已。

重繪視圖

當我們activity加載完成之后會自動繪制控件到屏幕上茎杂,但是我們經(jīng)常在后期做一些更改错览,比如listView的加載更多的數(shù)據(jù)等,就需要動態(tài)的隱藏煌往、添加一些控件等倾哺。

1、調(diào)用視圖的setVisibility() 刽脖、setEnabled()羞海、setSelected()方法都會導致視圖重繪。

想要手動的讓視圖重繪就需要調(diào)用invalidate()方法曲管,invalidate會判斷當前view是否需要重繪却邓,如果view是不可見的且沒有執(zhí)行任何動畫,就認為不需要重繪院水,之后會通過透明度判斷腊徙,并給view添加一些標記.

注意:在調(diào)用invalidate()方法后不會調(diào)用measure和layout為視圖強制測量的標志為简十,大小也不會有改變,這時候只有draw的繪制流程撬腾,如果需要完整的走一遍就不能使用invalidate()勺远,而是使用requestLayout().

再次感謝郭神的blog:http://blog.csdn.net/guolin_blog/article/details/16330267

本文只是對view的學習做個筆記謝謝!J蓖摇=悍辍!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饰潜,一起剝皮案震驚了整個濱河市初坠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彭雾,老刑警劉巖碟刺,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異薯酝,居然都是意外死亡半沽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門吴菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來者填,“玉大人,你說我怎么就攤上這事做葵≌加矗” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵酿矢,是天一觀的道長榨乎。 經(jīng)常有香客問我,道長瘫筐,這世上最難降的妖魔是什么蜜暑? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮策肝,結(jié)果婚禮上肛捍,老公的妹妹穿的比我還像新娘。我一直安慰自己驳糯,他們只是感情好篇梭,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酝枢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悍手。 梳的紋絲不亂的頭發(fā)上帘睦,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天袍患,我揣著相機與錄音,去河邊找鬼竣付。 笑死诡延,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的古胆。 我是一名探鬼主播肆良,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逸绎!你這毒婦竟也來了惹恃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤棺牧,失蹤者是張志新(化名)和其女友劉穎巫糙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颊乘,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡参淹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乏悄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浙值。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖檩小,靈堂內(nèi)的尸體忽然破棺而出亥鸠,到底是詐尸還是另有隱情,我是刑警寧澤识啦,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布负蚊,位于F島的核電站,受9級特大地震影響颓哮,放射性物質(zhì)發(fā)生泄漏家妆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一冕茅、第九天 我趴在偏房一處隱蔽的房頂上張望伤极。 院中可真熱鬧,春花似錦姨伤、人聲如沸哨坪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽当编。三九已至,卻和暖如春徒溪,著一層夾襖步出監(jiān)牢的瞬間忿偷,已是汗流浹背金顿。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲤桥,地道東北人揍拆。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像茶凳,于是被迫代替她去往敵國和親嫂拴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

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