Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android View measure (二) 自定義UI控件measure相關

Android View measure (二) 自定義UI控件measure相關

編輯:關於Android編程

 

本篇模擬三個角色:Android 架構師-小福、Android 控件開發工程師-小黑、 Android 開發工程師-小白,下面按照三個角色不同角度分析measure過程。

 

 

小福負責分享

 

measure的本質 measure代碼流程 onMeasure方法與MeasureSpec 提出問題

 

小黑負責分享

 

布局控件開發中覆寫Measure例子 - ok 從遇到的一個異常說起 什麼時候需要覆寫onMeaure? - ok view.getWidth與view.getMeasureWidth區別 - ok

 

Android 控件開發工程師-小黑的分享


一、布局控件開發中覆寫Measure例子

具體可查看之前寫的例子《覆寫onMeaure進行measure操作》,Android提供的布局FrameLaout、LinearLayout、RelativeLayout都是ViewGroup的子類,可以參考這些類的onMeausre實現,看看Android框架是如何處理的。

二、從遇到的一個異常說起

如果在onMeasure中使用return,並未進行setMeasuredDimension(width, height);類似操作,會出現以下異常
java.lang.IllegalStateException: onMeasure() did not set the measured dimension by calling setMeasuredDimension()

-待填充-
ViewGroup提供的measure方法 measureChildren() - 該函數內使用for()循環調用measureChild()對每一個子視圖進行measure操作 measureChild() - 為每一個子視圖進行measure操作 measureChildWidthMargins() - 該函數與measureChild的唯一區別在於,measure時考慮把margin及padding也作為子視圖大小的一部分

三、什麼時候需要覆寫onMeaure?

只要是自定義控件並且是ViewGroup都需要覆寫onMeasure來指定其測量視圖大小規則,但是Androd提供FrameLaout、LinearLayout、RelativeLayout等都是ViewGroup的子類並且都覆寫了onMeasure方法,自定義布局的時候可以先考慮先從這些類的子類入手,可能更簡單一些。

四、view.getWidth()與view.getMeasuredWidth()區別

從名字上可以看出這兩個方法都是為了獲取寬度的,相應的也有獲取高度的方法,但是問題在於兩者的區別是什麼? 下面直接從源碼中查看這些值是從哪裡來的,從而獲知兩者的區別。
首先看下View.java中的getMeasuredWidth方法,源碼如下:
public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,
        AccessibilityEventSource {

    /**
     * Like {@link #getMeasuredWidthAndState()}, but only returns the
     * raw width component (that is the result is masked by
     * {@link #MEASURED_SIZE_MASK}).
     *
     * @return The raw measured width of this view.
     */
    public final int getMeasuredWidth() {
        // 直接返回mMeasuredWidth與後者相與清理掉其他開關獲取真是measure大小
        return mMeasuredWidth & MEASURED_SIZE_MASK;
    }
	
    /**
     * 

This mehod must be called by {@link #onMeasure(int, int)} to store the * measured width and measured height. Failing to do so will trigger an * exception at measurement time.

* * @param measuredWidth The measured width of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. * @param measuredHeight The measured height of this view. May be a complex * bit mask as defined by {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { // 通常在onMeasure中調用,傳入測量過的視圖寬度與高度 mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= MEASURED_DIMENSION_SET; } /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. */ // MeasureSpec中的Mode或占用int類型中前幾位 public static final int MEASURED_SIZE_MASK = 0x00ffffff; }
從上面兩個方法可以看出getMeasuredWidth的值是從mMeasuredWidth變量獲取,而這個變量僅在View.setMeasuredDimension方法中繼續初始化,從setMeasuredDimension方法的注釋中就可以看出這個方式是在onMeasure中被調用,也就是getMeasuredWidth獲取到的是在視圖onMeasure方法中已經獲取到視圖的大小之後,才能進行賦值。getMeasuredWidth獲取的是通過onMeasure測量後的值,在onMeasure執行之前可以調用但是獲取到的都是0(int類型的默認初始化值)。
上面已經知道getMeasuredWidth值的含義,接著來看下View.getWidth方法的源碼:

public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,
        AccessibilityEventSource {
		
    /**
     * Return the width of the your view.
     *
     * @return The width of your view, in pixels.
     */
    @ViewDebug.ExportedProperty(category = "layout")
    public final int getWidth() {
        // 視圖的右側減去左側的值	
        return mRight - mLeft;
    }

    /**
     * Assign a size and position to this view.
     *
     * This is called from layout.
     *
     * @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
     * @return true if the new size and position are different than the
     *         previous ones
     * {@hide}
     */
    protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = false;

        ......
		
        // 四個值中任意一個發生改變就行		
        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = true;

            ......

            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;

            ......
        }
        return changed;
    }
	
    public void layout(int l, int t, int r, int b) {
        ......
        // 當前視圖布局時執行,傳入當前視圖的上下左右邊界值		
        boolean changed = setFrame(l, t, r, b);
		
        if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
            ......
            // 上面執行完成後才會觸發onLayout
            onLayout(changed, l, t, r, b);
			
            ......
        }
        ......
        mPrivateFlags &= ~FORCE_LAYOUT;
    }
}

從上面的代碼可以看出當視圖layout操作時,會先調用setFrame方法傳入left, top, right, bottom 這些值會在以後layout布局分析是進行詳細解釋,這些值是其父視圖給他當前視圖設定的可顯示位置與大小(right - left 與 top - bottom獲得)。 getWidth方法獲取的寬度是當前視圖可以在屏幕實際上占據的大小。
簡單總結下,getMeasuredWidth是視圖onMeasure指定的寬度(可以籠統的理解為視圖內容區域的大小,雖然不嚴謹但是系統提供的布局控件都是這樣,僅在自定義視圖中因為覆寫onMeasure可以忽略layout_width,layout_heigh隨意指定其寬高),而getWidth是視圖父視圖指定當前視圖可以在屏幕上顯示的區域。

上一篇/kf/201503/382697.html
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved