其實本來想看自定義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蓖摇=悍辍!