揭秘Android視圖繪制的流程步驟
什么是View?
View是Android系統(tǒng)中的一個(gè)基本組件,它是用戶界面上的一個(gè)矩形區(qū)域,可以用來展示文本、圖片、按鈕等等。View可以響應(yīng)用戶的交互事件,比如點(diǎn)擊、滑動(dòng)等等。在Android中,所有的UI組件都是繼承自View類。
View的繪制過程
View的繪制過程可以分為三個(gè)階段:測(cè)量、布局和繪制。下面我們將逐一介紹這三個(gè)階段。
測(cè)量階段(Measure)
測(cè)量階段是View繪制過程的第一個(gè)重要階段。在測(cè)量階段,系統(tǒng)會(huì)調(diào)用View的onMeasure方法,測(cè)量View的寬度和高度。在這個(gè)過程中,系統(tǒng)會(huì)根據(jù)View的LayoutParams和父容器的大小來計(jì)算出View的大小。
例:下面代碼是一個(gè)自定義View的onMeasure方法例程。在測(cè)量過程中,我們?cè)O(shè)定了View的大小。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 獲取寬度的Size和Mode
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
// 如果Mode是精確的,直接返回
if (widthMode == MeasureSpec.EXACTLY) {
setMeasuredDimension(widthSize, heightMeasureSpec);
return;
}
// 計(jì)算View的寬度
int desiredWidth = getPaddingLeft() + getPaddingRight() + defaultWidth;
int measuredWidth;
if (desiredWidth < widthSize) {
measuredWidth = desiredWidth;
} else {
measuredWidth = widthSize;
}
// 設(shè)置寬度和高度的Size和Mode
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int measuredHeight = defaultHeight;
if (heightMode == MeasureSpec.EXACTLY) {
measuredHeight = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
measuredHeight = Math.min(defaultHeight, heightSize);
}
setMeasuredDimension(measuredWidth, measuredHeight);
}在測(cè)量階段結(jié)束后,系統(tǒng)會(huì)將計(jì)算好的寬度和高度傳遞給布局階段。
布局階段(Layout)
布局階段是View繪制過程的第二個(gè)重要階段。在布局階段,系統(tǒng)會(huì)調(diào)用View的onLayout方法,將View放置在父容器中的正確位置。在這個(gè)過程中,系統(tǒng)會(huì)根據(jù)View的LayoutParams和父容器的位置來確定View的位置。
例:下面代碼是一個(gè)自定義ViewGroup的onLayout方法例程。在布局過程中,我們遍歷子View,并根據(jù)LayoutParams確定子View的位置和大小。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
int left = getPaddingLeft();
int top = getPaddingTop();
int right = getMeasuredWidth() - getPaddingRight();
int bottom = getMeasuredHeight() - getPaddingBottom();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childLeft = left + lp.leftMargin;
int childTop = top + lp.topMargin;
int childRight = right - lp.rightMargin;
int childBottom = bottom - lp.bottomMargin;
child.layout(childLeft, childTop, childRight, childBottom);
}
}繪制階段(Draw)
繪制階段是View繪制過程的最后一個(gè)重要階段。在繪制階段,系統(tǒng)會(huì)調(diào)用View的onDraw方法,繪制View的內(nèi)容。在這個(gè)過程中,我們可以使用Canvas對(duì)象來繪制各種形狀、文本和圖片等等。
例:下面代碼是一個(gè)自定義View的onDraw方法例程。在繪制過程中,我們使用Paint對(duì)象繪制了一段文本。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制文本
String text = "Hello World";
Paint paint = new Paint();
paint.setTextSize(50);
paint.setColor(Color.RED);
paint.setAntiAlias(true);
canvas.drawText(text, 0, getHeight() / 2, paint);
}除了繪制內(nèi)容,我們還可以在繪制階段繪制View的背景和前景。系統(tǒng)會(huì)調(diào)用drawBackground和drawForeground方法來繪制背景和前景。值得注意的是,View的繪制順序是:先繪制背景,再繪制內(nèi)容,最后繪制前景。
View的繪制流程
View的繪制流程可以看作是一個(gè)遞歸調(diào)用的過程,下面我們將具體介紹這個(gè)過程。
Step 1:創(chuàng)建View
在View繪制過程的開始階段,我們需要?jiǎng)?chuàng)建一個(gè)View對(duì)象,并將它添加到父容器中。在這個(gè)過程中,系統(tǒng)會(huì)調(diào)用View的構(gòu)造函數(shù),并將View的LayoutParams傳遞給它。
Step 2:測(cè)量View
接下來,系統(tǒng)會(huì)調(diào)用View的measure方法,測(cè)量View的寬度和高度。在這個(gè)過程中,View會(huì)根據(jù)自身的LayoutParams和父容器的大小來計(jì)算出自己的寬度和高度。
Step 3:布局View
在測(cè)量完成后,系統(tǒng)會(huì)調(diào)用View的layout方法,將View放置在父容器中的正確位置。在這個(gè)過程中,View會(huì)根據(jù)自身的LayoutParams和父容器的位置來確定自己的位置。
Step 4:繪制背景
在布局完成后,系統(tǒng)會(huì)調(diào)用View的drawBackground方法,繪制View的背景。在這個(gè)過程中,我們可以使用Canvas對(duì)象來繪制各種形狀、文本和圖片等等。
Step 5:繪制內(nèi)容
接下來,系統(tǒng)會(huì)調(diào)用View的onDraw方法,繪制View的內(nèi)容。在這個(gè)過程中,我們可以使用Canvas對(duì)象來繪制各種形狀、文本和圖片等等。
Step 6:繪制前景
在繪制內(nèi)容完成后,系統(tǒng)會(huì)調(diào)用View的drawForeground方法,繪制View的前景。在這個(gè)過程中,我們同樣可以使用Canvas對(duì)象來繪制各種形狀、文本和圖片等等。
Step 7:繪制子View
接著,系統(tǒng)會(huì)遞歸調(diào)用ViewGroup的dispatchDraw方法,繪制所有子View的內(nèi)容。在這個(gè)過程中,我們可以使用Canvas對(duì)象來繪制各種形狀、文本和圖片等等。
Step 8:完成繪制
最后,所有的View繪制完成,整個(gè)View樹也就繪制完成。
例:下面代碼是一個(gè)自定義ViewGroup的繪制流程例程。在繪制過程中,我們先畫背景,再繪制每個(gè)子View的內(nèi)容。
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 測(cè)量子View的寬高
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 獲取ViewGroup的寬高大小
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 設(shè)置ViewGroup的寬高
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 遍歷所有子View,設(shè)置它們的位置和大小
int childCount = getChildCount();
int left, top, right, bottom;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
left = childView.getLeft();
top = childView.getTop();
right = childView.getRight();
bottom = childView.getBottom();
childView.layout(left, top, right, bottom);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 畫背景
canvas.drawColor(Color.WHITE);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// 繪制每個(gè)子View的內(nèi)容
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
childView.draw(canvas);
}
}
}在ViewGroup的繪制流程中,系統(tǒng)會(huì)先調(diào)用ViewGroup的draw方法,然后依次調(diào)用dispatchDraw方法和繪制每個(gè)子View的draw方法。ViewGroup的繪制順序是先繪制自己的背景,再繪制每個(gè)子View的內(nèi)容和背景,最后繪制自己的前景。
總結(jié)
本文詳細(xì)介紹了Android View的繪制過程,包括測(cè)量階段、布局階段和繪制階段。同時(shí),我們還在代碼實(shí)現(xiàn)的角度,詳細(xì)說明了Android ViewGroup的繪制流程,幫助你更好地理解和掌握Android的UI開發(fā)。
以上就是揭秘Android視圖繪制的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于Android 視圖繪制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開發(fā)之APP安裝后在桌面上不顯示應(yīng)用圖標(biāo)的解決方法
這篇文章主要介紹了Android開發(fā)之APP安裝后在桌面上不顯示應(yīng)用圖標(biāo)的解決方法,涉及Android activity相關(guān)屬性設(shè)置技巧,需要的朋友可以參考下2017-07-07
Android實(shí)現(xiàn)三段式滑動(dòng)效果
最近發(fā)現(xiàn)很多app都使用了三段式滑動(dòng),比如說高德的首頁和某寶等物流信息都是使用的三段式滑動(dòng)方式,谷歌其實(shí)給了我們很好的2段式滑動(dòng),就是BottomSheet,所以這次我也是在這個(gè)原理基礎(chǔ)上做了一個(gè)小小的修改來實(shí)現(xiàn)我們今天想要的效果。2021-06-06
Android 中為什么要用Fragment.setArguments(Bundle bundle)來傳遞參數(shù)
這篇文章主要介紹了Android 中為什么要用Fragment.setArguments(Bundle bundle)來傳遞參數(shù),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01
Android中RecyclerView實(shí)現(xiàn)滑動(dòng)刪除與拖拽功能
這篇文章主要使用了RecyclerView的ItemTouchHelper類實(shí)現(xiàn)了Item的拖動(dòng)和刪除功能,ItemTouchHelper是v7包下的一個(gè)類,下面來看看詳細(xì)的介紹吧,需要的朋友可以參考學(xué)習(xí)。2017-02-02
詳解Android WebView的input上傳照片的兼容問題
本篇文章主要介紹了詳解Android WebView的input上傳照片的兼容問題,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-08-08
Android編程簡單解析JSON格式數(shù)據(jù)的方法示例
這篇文章主要介紹了Android編程簡單解析JSON格式數(shù)據(jù)的方法,結(jié)合實(shí)例形式分析了Android編程解析json格式數(shù)據(jù)的實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Android一步步帶你在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"功能
這篇文章主要介紹了Android一步步帶你在RecyclerView上面實(shí)現(xiàn)"拖放"和"滑動(dòng)刪除"功能,需非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-03-03
詳解Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表
這篇文章主要為大家詳細(xì)介紹了Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表,這些列表可以水平排列、網(wǎng)格格式、垂直排列,甚至是這些常用布局的組合,感興趣的小伙伴可以了解下2023-11-11

