揭秘Android視圖繪制的流程步驟
什么是View?
View是Android系統(tǒng)中的一個(gè)基本組件,它是用戶(hù)界面上的一個(gè)矩形區(qū)域,可以用來(lái)展示文本、圖片、按鈕等等。View可以響應(yīng)用戶(hù)的交互事件,比如點(diǎn)擊、滑動(dòng)等等。在Android中,所有的UI組件都是繼承自View類(lèi)。
View的繪制過(guò)程
View的繪制過(guò)程可以分為三個(gè)階段:測(cè)量、布局和繪制。下面我們將逐一介紹這三個(gè)階段。
測(cè)量階段(Measure)
測(cè)量階段是View繪制過(guò)程的第一個(gè)重要階段。在測(cè)量階段,系統(tǒng)會(huì)調(diào)用View的onMeasure
方法,測(cè)量View的寬度和高度。在這個(gè)過(guò)程中,系統(tǒng)會(huì)根據(jù)View的LayoutParams和父容器的大小來(lái)計(jì)算出View的大小。
例:下面代碼是一個(gè)自定義View的onMeasure方法例程。在測(cè)量過(guò)程中,我們?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繪制過(guò)程的第二個(gè)重要階段。在布局階段,系統(tǒng)會(huì)調(diào)用View的onLayout
方法,將View放置在父容器中的正確位置。在這個(gè)過(guò)程中,系統(tǒng)會(huì)根據(jù)View的LayoutParams和父容器的位置來(lái)確定View的位置。
例:下面代碼是一個(gè)自定義ViewGroup的onLayout方法例程。在布局過(guò)程中,我們遍歷子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繪制過(guò)程的最后一個(gè)重要階段。在繪制階段,系統(tǒng)會(huì)調(diào)用View的onDraw
方法,繪制View的內(nèi)容。在這個(gè)過(guò)程中,我們可以使用Canvas對(duì)象來(lái)繪制各種形狀、文本和圖片等等。
例:下面代碼是一個(gè)自定義View的onDraw方法例程。在繪制過(guò)程中,我們使用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
方法來(lái)繪制背景和前景。值得注意的是,View的繪制順序是:先繪制背景,再繪制內(nèi)容,最后繪制前景。
View的繪制流程
View的繪制流程可以看作是一個(gè)遞歸調(diào)用的過(guò)程,下面我們將具體介紹這個(gè)過(guò)程。
Step 1:創(chuàng)建View
在View繪制過(guò)程的開(kāi)始階段,我們需要?jiǎng)?chuàng)建一個(gè)View對(duì)象,并將它添加到父容器中。在這個(gè)過(guò)程中,系統(tǒng)會(huì)調(diào)用View的構(gòu)造函數(shù),并將View的LayoutParams傳遞給它。
Step 2:測(cè)量View
接下來(lái),系統(tǒng)會(huì)調(diào)用View的measure
方法,測(cè)量View的寬度和高度。在這個(gè)過(guò)程中,View會(huì)根據(jù)自身的LayoutParams和父容器的大小來(lái)計(jì)算出自己的寬度和高度。
Step 3:布局View
在測(cè)量完成后,系統(tǒng)會(huì)調(diào)用View的layout
方法,將View放置在父容器中的正確位置。在這個(gè)過(guò)程中,View會(huì)根據(jù)自身的LayoutParams和父容器的位置來(lái)確定自己的位置。
Step 4:繪制背景
在布局完成后,系統(tǒng)會(huì)調(diào)用View的drawBackground
方法,繪制View的背景。在這個(gè)過(guò)程中,我們可以使用Canvas對(duì)象來(lái)繪制各種形狀、文本和圖片等等。
Step 5:繪制內(nèi)容
接下來(lái),系統(tǒng)會(huì)調(diào)用View的onDraw
方法,繪制View的內(nèi)容。在這個(gè)過(guò)程中,我們可以使用Canvas對(duì)象來(lái)繪制各種形狀、文本和圖片等等。
Step 6:繪制前景
在繪制內(nèi)容完成后,系統(tǒng)會(huì)調(diào)用View的drawForeground
方法,繪制View的前景。在這個(gè)過(guò)程中,我們同樣可以使用Canvas對(duì)象來(lái)繪制各種形狀、文本和圖片等等。
Step 7:繪制子View
接著,系統(tǒng)會(huì)遞歸調(diào)用ViewGroup的dispatchDraw
方法,繪制所有子View的內(nèi)容。在這個(gè)過(guò)程中,我們可以使用Canvas對(duì)象來(lái)繪制各種形狀、文本和圖片等等。
Step 8:完成繪制
最后,所有的View繪制完成,整個(gè)View樹(shù)也就繪制完成。
例:下面代碼是一個(gè)自定義ViewGroup的繪制流程例程。在繪制過(guò)程中,我們先畫(huà)背景,再繪制每個(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); // 畫(huà)背景 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的繪制過(guò)程,包括測(cè)量階段、布局階段和繪制階段。同時(shí),我們還在代碼實(shí)現(xiàn)的角度,詳細(xì)說(shuō)明了Android ViewGroup的繪制流程,幫助你更好地理解和掌握Android的UI開(kāi)發(fā)。
以上就是揭秘Android視圖繪制的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于Android 視圖繪制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android RecyclerView的一些優(yōu)化點(diǎn)介紹
大家好,本篇文章主要講的是android RecyclerView的一些優(yōu)化點(diǎn)介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽2021-12-12Android編程實(shí)現(xiàn)震動(dòng)與振鈴的方法詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)震動(dòng)與振鈴的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)震動(dòng)與振鈴的Vibrator類(lèi)及MediaPlayer類(lèi)相關(guān)使用技巧,需要的朋友可以參考下2018-03-03總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化
網(wǎng)上有很多大拿分享的關(guān)于Android性能優(yōu)化的文章,主要是通過(guò)各種工具分析,使用合理的技巧優(yōu)化APP的體驗(yàn),提升APP的流暢度,但關(guān)于內(nèi)存優(yōu)化的文章很少有看到。下面是我在實(shí)踐過(guò)程中使用的一些方法,很多都是不太成熟的項(xiàng)目,只是將其作為一種處理方式分享給大家。2016-08-08Android studio 2020中的Android SDK 下載教程
這篇文章主要介紹了Android studio 2020中的Android SDK 下載教程,本文圖文并茂給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android的Activity跳轉(zhuǎn)動(dòng)畫(huà)各種效果整理
Android的Activity跳轉(zhuǎn)就是很生硬的切換界面。其實(shí)Android的Activity跳轉(zhuǎn)可以設(shè)置各種動(dòng)畫(huà),本文整理了一些,還有很多動(dòng)畫(huà)效果,就要靠我們發(fā)揮自己的想象力2013-06-06Android GPS室內(nèi)定位問(wèn)題的解決方法(location為null)
這篇文章主要為大家詳細(xì)介紹了Android GPS室內(nèi)定位問(wèn)題的解決方法,location為null,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04