Android自定義view詳解及Measurepec深入解析
理解自定義View的三大流程
自定義View的繪制主要圍繞三個核心過程展開,它們依次執(zhí)行,共同決定了View的最終呈現(xiàn):
流程階段 | 核心方法 | 主要職責(zé) |
|---|---|---|
測量 (Measure) |
| 確定View的測量寬度和高度。必須在此方法末尾調(diào)用 |
布局 (Layout) |
| 確定View在父容器中的位置(四個頂點的坐標),對于ViewGroup,還需負責(zé)遍歷和確定所有子View的位置。 |
繪制 (Draw) |
| 將View的視覺內(nèi)容繪制到屏幕上。通過 |
整個流程的發(fā)起者是ViewRootImpl的performTraversals()方法,它會根據(jù)情況決定是否執(zhí)行完整的測量、布局和繪制。
深入解析MeasureSpec
MeasureSpec(測量規(guī)格)是理解測量流程的鑰匙。它是一個32位的int值,高2位代表測量模式(SpecMode),低30位代表在該模式下的規(guī)格大小(SpecSize)。其設(shè)計目的是為了高效地用一個變量同時攜帶模式和尺寸信息。
1. 三種測量模式的含義
模式 | 含義 | 常見對應(yīng)場景 |
|---|---|---|
EXACTLY | 精確模式:父容器已經(jīng)為子View確定了一個精確的尺寸。此時子View的尺寸應(yīng)直接設(shè)為 | 在布局中設(shè)置了具體數(shù)值(如 |
AT_MOST | 最大模式:父容器為子View指定了一個最大可用尺寸。子View的尺寸不能超過這個 | 在布局中設(shè)置了 |
UNSPECIFIED | 未指定模式:父容器對子View沒有任何限制,子View可以取任意它需要的大小。這種模式不常用,通常出現(xiàn)在 |
2. MeasureSpec的確定規(guī)則
一個子View的MeasureSpec并非憑空產(chǎn)生,而是由父容器的MeasureSpec和子View自身的LayoutParams(布局參數(shù),如match_parent、wrap_content或固定尺寸)共同決定的。這個規(guī)則封裝在ViewGroup的getChildMeasureSpec()方法中。
其決策規(guī)律可以總結(jié)為下表:
子View的LayoutParams | 父容器的SpecMode | 子View的SpecMode |
|---|---|---|
固定值(如100dp) | 任意模式 |
|
match_parent |
|
|
|
| |
wrap_content |
|
|
實現(xiàn)自定義View的關(guān)鍵步驟
1. 繼承View類并重寫構(gòu)造方法
通常需要實現(xiàn)三個構(gòu)造函數(shù),以正確處理在代碼中創(chuàng)建和在XML布局中聲明的情況。
public class MyCustomView extends View {
public MyCustomView(Context context) {
super(context);
init();
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化畫筆、屬性等
}
}2. 正確處理測量(重寫onMeasure)
這是自定義View,特別是處理wrap_content時最容易出錯的地方。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int defaultWidth = 200; // 自定義View的默認寬度
int defaultHeight = 200; // 自定義View的默認高度
int width = resolveSize(defaultWidth, widthMeasureSpec);
int height = resolveSize(defaultHeight, heightMeasureSpec);
setMeasuredDimension(width, height);
}關(guān)鍵點:如果不重寫onMeasure,或者處理不當,當在布局中使用wrap_content時,其效果會與match_parent相同。因為系統(tǒng)的默認實現(xiàn)(getDefaultSize())在AT_MOST和EXACTLY模式下都返回相同的SpecSize。因此,你需要為wrap_content(即AT_MOST模式)指定一個默認大小。
3. 實現(xiàn)繪制(重寫onDraw)
在此方法中,使用Canvas和Paint進行繪制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 示例:繪制一個圓形
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int radius = Math.min(centerX, centerY) - 10;
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX, centerY, radius, paint);
}注意:應(yīng)考慮padding的影響,繪制內(nèi)容時應(yīng)減去相應(yīng)的padding值,否則padding屬性會失效。
進階技巧與優(yōu)化建議
- 性能優(yōu)化:
- 避免在
onDraw中創(chuàng)建對象:例如Paint,應(yīng)在初始化時(如構(gòu)造方法)創(chuàng)建并復(fù)用。 - 使用
Canvas.clipRect():避免繪制View邊界之外的內(nèi)容,減少過度繪制。 - 考慮使用硬件加速:在XML中設(shè)置
android:layerType="hardware"可以提高某些繪制操作的性能。
- 避免在
- 自定義屬性:你可以為自定義View定義屬性,使其更靈活。步驟包括:在
res/values/下創(chuàng)建attrs.xml定義屬性;在構(gòu)造方法中使用TypedArray解析屬性;在布局文件中使用自定義命名空間聲明屬性。 - 處理觸摸事件:通過重寫
onTouchEvent(MotionEvent event)方法,可以讓你的View具有交互能力。
到此這篇關(guān)于Android的自定義view詳解及Measurepec詳解的文章就介紹到這了,更多相關(guān)android的自定義view內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
RXjava網(wǎng)絡(luò)獲取圖片數(shù)據(jù)的方法
這篇文章主要為大家詳細介紹了RXjava網(wǎng)絡(luò)獲取圖片數(shù)據(jù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08
Android 中ListView setOnItemClickListener點擊無效原因分析
這篇文章主要介紹了Android 中ListView setOnItemClickListener點擊無效原因分析的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android 中ActionBar+fragment實現(xiàn)頁面導(dǎo)航的實例
這篇文章主要介紹了Android 中ActionBar+fragment實現(xiàn)頁面導(dǎo)航的實例的相關(guān)資料,希望通過本文能幫助到大家實現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09

