編輯:關於Android編程
測量控件尺寸(寬度、高度)是開發自定義控件的第一步,只有確定尺寸後才能開始畫(利用canvas在畫布上畫,我們所使用的控件實際上都是這樣畫上去的)。當然,這個尺寸是需要根據控件的各個部分計算出來的,比如:padding、文字大小,間距等。
非容器控件的onMeasure
下面我們就來看看如何給非容器控件(即直接extends View)這只尺寸的:
1.
@Override
2.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
3.
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));4.
}
通過重寫onMeasure()方法來設置尺寸。這個方法實際是由容器控件(LinearLayout、RelativeLayout)來調用的,以便容器控件知道我需要分配給你多大空間或尺寸。
計算完尺寸後,最終調用setMeasuredDimension()來設置。比如,我要創建個 寬度是200px,高度是100px的控件,就可以setMeasuredDimension(200, 100)。
注意setMeasuredDimension()接收的值是px值,而不是dp值,所以我們不可能像上面那樣將尺寸寫固定的。如何做那:
參數 int widthMeasureSpec, int heightMeasureSpec 是指明控件可獲得的空間以及關於這個空間描述的元數據,是與布局文件中android:layout_width、android:layout_height相聯系的。如何使用:
01.
/**
02.
* 計算組件寬度
03.
*/
04.
private
int
measureWidth(
int
widthMeasureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(widthMeasureSpec);
07.
int
specSize = MeasureSpec.getSize(widthMeasureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
//精確模式
10.
result = specSize;
11.
}
else
{
12.
result = getDefaultWidth();//最大尺寸模式,getDefaultWidth方法需要我們根據控件實際需要自己實現
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
調用MeasureSpec.getMode(measureSpec),可以獲得設置尺寸的mode(模式)。
mode共有三種情況,取值分別為:
MeasureSpec.UNSPECIFIED,MeasureSpec.EXACTLY,MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精確尺寸,當我們將控件的layout_width或layout_height指定為具體數值時如andorid:layout_width=50dip,或者為FILL_PARENT是,都是控件大小已經確定的情況,都是精確尺寸。
MeasureSpec.AT_MOST是最大尺寸,當控件的layout_width或layout_height指定為WRAP_CONTENT時,控件大小一般隨著控件的子空間或內容進行變化,此時控件尺寸只要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。
measureWidth() 方法是個固定實現,幾乎不用改, 唯一需要我們實現的就是getDefaultWidth()。即,當mode處於最大模式下,此時父容器會將他所能給你的或者說目前剩余的最大空間給你。你只能在這個空間內設定控件尺寸。一個簡單的類似TextView的getDefaultWidth()的實現:
1.
private
void
getDefaultWidth(){
2.
int
txtWidth = (
int
)
this
.paint.measureText(
this
.text);
3.
return
txtWidth +
this
.paddingLeft +
this
.paddingRight;
4.
}
上面說了寬度的測量,高度同理:
01.
/**
02.
* 計算組件高度
03.
*/
04.
private
int
measureHeight(
int
measureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(measureSpec);
07.
int
specSize = MeasureSpec.getSize(measureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
10.
result = specSize;
11.
}
else
{
12.
result = getDefaultHeight();
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
有了上面的實現,我們就可以在布局文件中使用控件時,通過android:layout_width、android:layout_height來自定義控件的寬度、高度。
容器控件的onMeasure
容器控件一般是繼承ViewGroup,ViewGroup是個抽象類,本身沒有實現onMeasure,但是他的子類都有各自的實現,通常他們都是通過measureChildWithMargins函數或者其他類似於measureChild的函數來遍歷測量子View,被GONE的子View將不參與測量,當所有的子View都測量完畢後,才根據父View傳遞過來的模式和大小來最終決定自身的大小.
在測量子View時,會先獲取子View的LayoutParams,從中取出寬高,如果是大於0,將會以精確的模式加上其值組合成MeasureSpec傳遞子View,如果是小於0,將會把自身的大小或者剩余的大小傳遞給子View,其模式判定在前面表中有對應關系.
ViewGroup一般都在測量完所有子View後才會調用setMeasuredDimension()設置自身大小。
不是自己不想總結,是因為這篇博客總結的太好了,自己總結估計總結不到這麼全。所以轉來分享。謝謝該博主的共享精神。開篇如下:在Android中實現異步任務機制有兩種方式,Ha
概述: 基於上一篇博客《Android退出程序(上)——單例模式》的學習,我們知道了如何利用單例和循環遍歷的方式來退出我們的應用程序。這篇博客則要從另一個角度來解決問題—
Auticompelete TextView動態匹配輸入的內容:目的,動態匹配輸入的內容,如百度搜索引擎當輸入文本時可以根據內容顯示匹配的熱門信息。一.目的效果圖:實驗效
0x00 序隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。另外工具是死的,人是活的,如果能搞懂工具的原理再結合上自身的經驗,