View視圖安卓應(yīng)用中非常重要的組成部分,它不僅是構(gòu)成應(yīng)用界面的基本單元,還是與用戶交互的最直接對(duì)象。視圖View作為界面的基本元素,是由View System進(jìn)行管理的。
成都創(chuàng)新互聯(lián)專注于商都網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供商都營銷型網(wǎng)站建設(shè),商都網(wǎng)站制作、商都網(wǎng)頁設(shè)計(jì)、商都網(wǎng)站官網(wǎng)定制、小程序設(shè)計(jì)服務(wù),打造商都網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供商都網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
在Android中,視圖控件主要被分成兩大類,一類是單一控件View,另外一類是可以包含并管理子控件的ViewGroup,在一個(gè)界面布局中,ViewGroup與View的嵌套與組合,就構(gòu)成了一棵控件樹,經(jīng)過一系列流程繪制在屏幕上,形成完整的用戶界面。
View是最基本的UI類,幾乎所有的控件都繼承于View,廣義的View指的是除了ViewGroup外的如TextView,CheckBox、Button、EditText、RadioButton等的單一控件,它們管理自身的繪制以及對(duì)事件的處理。而ViewGroup是View 的子類,同時(shí)作為View控件的容器而存在,ViewGroup可以控制各個(gè)子View 的顯示層次與位置,同時(shí)可以對(duì)焦點(diǎn)獲取、觸摸等交互事件進(jìn)行處理、攔截或分發(fā)。
視圖的繪制主要分為三個(gè)步驟,分別為Measure,Layout跟Draw。
在Measure操作中,測量的操作的分發(fā)由ViewGroup來完成,ViewGroup通過對(duì)子節(jié)點(diǎn)進(jìn)行遍歷并分發(fā)測量操作,在具體的測量過程中,根據(jù)父節(jié)點(diǎn)的MeasureSpec以及子節(jié)點(diǎn)的LayoutParams等信息計(jì)算子節(jié)點(diǎn)的寬高,最終整理成父容器的寬高,具體的測量方式,我們也可以通過重寫onMeasure方法來設(shè)定。
緊接著,通過Layout過程,來確定每一個(gè)View在父容器中的具體位置,同樣的,我們也可以通過onLayout方法來自定義具體的布局流程。
最后進(jìn)入Draw的繪制流程,根據(jù)前兩步所獲得的具體布局參數(shù),在draw函數(shù)中對(duì)各控件進(jìn)行繪制,繪制的順序?yàn)楸尘?控件內(nèi)容-子控件繪制-繪制邊緣以及滾動(dòng)條等裝飾物。
總體來說,控件的繪制都是在控件樹上進(jìn)行的,由ViewGroup分發(fā)給子View各自完成自身的測量與布局操作,最后由根節(jié)點(diǎn)開始進(jìn)行繪制,最終形成完整的界面。
最后選擇一個(gè)具體的例子來觀察View的繪制流程吧。
首先我們看一下TextView的具體繪制流程:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
......
setMeasuredDimension(width, height);
}
傳入的MeasureSpec是一個(gè)32位的整數(shù),高兩位表示測量模式,低30位表示規(guī)格大小,這個(gè)整數(shù)指定了控件將如何進(jìn)行具體的測量。最后利用更新后的width跟height設(shè)置測量結(jié)果的寬高。這個(gè)函數(shù)的主要作用是計(jì)算是否有margin/padding等造成額外的寬高,在必要時(shí)預(yù)留空間,并確定view的大小。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mDeferScroll >= 0) {
int curs = mDeferScroll;
mDeferScroll = -1;
bringPointIntoView(Math.min(curs, mText.length()));
}
}
在這里調(diào)用了父類的onLayout函數(shù)
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout
(boolean changed, int left, int top, int right, int bottom) {
}
可以看到View中該函數(shù)的注釋,可以知道這個(gè)函數(shù)的主要作用是對(duì)view的位置與大小進(jìn)行賦值,以便下一步進(jìn)行繪制。
@Override
protected void onDraw(Canvas canvas) {
restartMarqueeIfNeeded();
// Draw the background for this view
super.onDraw(canvas);
......
mTextPaint.setColor(color);
mTextPaint.drawableState = getDrawableState();
......
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
......
}
onDraw函數(shù)在該view需要進(jìn)行渲染時(shí)調(diào)用,設(shè)置了具體的畫筆等參數(shù),并調(diào)用draw進(jìn)行具體繪制。
/**
* Draw this Layout on the specified canvas, with the highlight path drawn
* between the background and the text.
*
* @param canvas the canvas
* @param highlight the path of the highlight or cursor; can be null
* @param highlightPaint the paint for the highlight
* @param cursorOffsetVertical the amount to temporarily translate the
* canvas while rendering the highlight
*/
public void draw(Canvas canvas, Path highlight, Paint highlightPaint,
int cursorOffsetVertical) {
final long lineRange = getLineRangeForDraw(canvas);
int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
firstLine, lastLine);
drawText(canvas, firstLine, lastLine);
}
在draw方法中分別調(diào)用對(duì)背景以及具體文字的繪制函數(shù)。
總而言之,我們可以重寫這些方法,來完成我們的控件自定義操作。