溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android-UI控件的繪制流程以及自定義控件的具體操作

發(fā)布時(shí)間:2020-06-21 11:06:57 來源:網(wǎng)絡(luò) 閱讀:1676 作者:DavidWillo 欄目:移動(dòng)開發(fā)

  View視圖安卓應(yīng)用中非常重要的組成部分,它不僅是構(gòu)成應(yīng)用界面的基本單元,還是與用戶交互的最直接對(duì)象。視圖View作為界面的基本元素,是由View System進(jìn)行管理的。

  在Android中,視圖控件主要被分成兩大類,一類是單一控件View,另外一類是可以包含并管理子控件的ViewGroup,在一個(gè)界面布局中,ViewGroup與View的嵌套與組合,就構(gòu)成了一棵控件樹,經(jīng)過一系列流程繪制在屏幕上,形成完整的用戶界面。

Android-UI控件的繪制流程以及自定義控件的具體操作

   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操作中,測(cè)量的操作的分發(fā)由ViewGroup來完成,ViewGroup通過對(duì)子節(jié)點(diǎn)進(jìn)行遍歷并分發(fā)測(cè)量操作,在具體的測(cè)量過程中,根據(jù)父節(jié)點(diǎn)的MeasureSpec以及子節(jié)點(diǎn)的LayoutParams等信息計(jì)算子節(jié)點(diǎn)的寬高,最終整理成父容器的寬高,具體的測(cè)量方式,我們也可以通過重寫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各自完成自身的測(cè)量與布局操作,最后由根節(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ù),高兩位表示測(cè)量模式,低30位表示規(guī)格大小,這個(gè)整數(shù)指定了控件將如何進(jìn)行具體的測(cè)量。最后利用更新后的width跟height設(shè)置測(cè)量結(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ù)。 

  總而言之,我們可以重寫這些方法,來完成我們的控件自定義操作。 


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI