Android View measure (二) 自訂UI控制項measure相關,androidmeasure

來源:互聯網
上載者:User

Android View measure (二) 自訂UI控制項measure相關,androidmeasure

 

  本篇類比三個角色: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;    }    /**     * <p>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.</p>     *     * @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是視圖父視圖指定當前視圖可以在螢幕上顯示的地區。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.