Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android measure過程分析

Android measure過程分析

編輯:關於Android編程

 作為一名Android開發人員,我們都知道一個View從無到有,會經歷3個階段:   1. measure/測量階段,也就是確定某個view大小的過程;   2. layout/布局階段,也就是確定其左上右下坐標的過程;   3. draw/繪制階段,也就是按照前面2步計算的結果,將view繪制在屏幕相應的位置上;   今天,我帶領大家來看看View系統的measure過程。到現在相信大部分人都知道measure是從ViewRootImpl.measureHierarchy   方法開始的,但歸根結底是從performTraversals開始的。     為了從一開始就清楚onMeasure(int widthMeasureSpec, int heightMeasureSpec)的這2個參數從哪來的,雖然我們都知道   這2個參數表示parent施加給我們的約束,但可能大部分人不明白程序run起來的時候這些值都是從哪裡來的。為了弄清楚這個問題,   我們還得從上面ViewRootImpl的measureHierarchy說起,來看其源碼:   復制代碼     private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,             final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {         int childWidthMeasureSpec;         int childHeightMeasureSpec;         boolean windowSizeMayChange = false;           if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,                 "Measuring " + host + " in display " + desiredWindowWidth                 + "x" + desiredWindowHeight + "...");           boolean goodMeasure = false;         if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { // 在WRAP_CONTENT的情況下,先從一個prefered值開始measure             // On large screens, we don't want to allow dialogs to just             // stretch to fill the entire width of the screen to display             // one line of text.  First try doing the layout at a smaller             // size to see if it will fit.             final DisplayMetrics packageMetrics = res.getDisplayMetrics();             res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);             int baseSize = 0;             if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {                 baseSize = (int)mTmpValue.getDimension(packageMetrics);             }             if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);             if (baseSize != 0 && desiredWindowWidth > baseSize) { // 如果baseSize真小的話,用baseSize先measure一遍試試                 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("                         + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {                     goodMeasure = true; // measure的結果合適                 } else {                     // Didn't fit in that size... try expanding a bit.                     baseSize = (baseSize+desiredWindowWidth)/2; // 加大baseSize重新執行上述過程                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="                             + baseSize);                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);                     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {                         if (DEBUG_DIALOG) Log.v(TAG, "Good!");                         goodMeasure = true;                     }                 }             }         }           if (!goodMeasure) { // 如果用baseSize measure的結果不合適,則老老實實用提供的參數重新measure一遍             childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);             childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);             performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);             if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {                 windowSizeMayChange = true;             }         }           if (DBG) {             System.out.println("======================================");             System.out.println("performTraversals -- after measure");             host.debug();         }           return windowSizeMayChange;     }       /**      * Figures out the measure spec for the root view in a window based on it's      * layout params.      *      * @param windowSize      *            The available width or height of the window      *      * @param rootDimension      *            The layout params for one dimension (width or height) of the      *            window.      *      * @return The measure spec to use to measure the root view.      */     private static int getRootMeasureSpec(int windowSize, int rootDimension) { // 通過具體的windowSize和提供的spec         int measureSpec;                                                       // 構造一個合適的Root MeasureSpec         switch (rootDimension) {  // 這裡的windowSize就是設備的寬、高,rootDimension就是xml文件裡指定的layoutparam           case ViewGroup.LayoutParams.MATCH_PARENT:             // Window can't resize. Force root view to be windowSize. 設置root view就是window這麼大             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);             break;         case ViewGroup.LayoutParams.WRAP_CONTENT:             // Window can resize. Set max size for root view. 設置root view最多是window這麼大             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);             break;         default:             // Window wants to be an exact size. Force root view to be that size. 某一個具體的大小             measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);             break;         }         return measureSpec;     }       private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");         try {             // 調用root view的measure方法,從此進入到view層次結構,順便也把MeasureSpec帶了進去。。。             mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);         } finally {             Trace.traceEnd(Trace.TRACE_TAG_VIEW);         }     } 復制代碼   從之前的文章中,我們知道DecorView實際上是繼承至FrameLayout,由於它和ViewGroup都沒有重載這個方法,實際上也沒法重載,   因為這個方法是View的一個final方法,代碼如下:   復制代碼 /**      * <p>      * This is called to find out how big a view should be. The parent      * supplies constraint information in the width and height parameters.      * </p>      *      * <p>      * The actual measurement work of a view is performed in      * {@link #onMeasure(int, int)}, called by this method. Therefore, only      * {@link #onMeasure(int, int)} can and must be overridden by subclasses.      * </p>      *      *      * @param widthMeasureSpec Horizontal space requirements as imposed by the      *        parent      * @param heightMeasureSpec Vertical space requirements as imposed by the      *        parent      *      * @see #onMeasure(int, int)      */     public final void measure(int widthMeasureSpec, int heightMeasureSpec) {         boolean optical = isLayoutModeOptical(this);         if (optical != isLayoutModeOptical(mParent)) {             Insets insets = getOpticalInsets();             int oWidth  = insets.left + insets.right;             int oHeight = insets.top  + insets.bottom;             widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);             heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);         }           // Suppress sign extension for the low bytes         long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;         if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);           if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                 widthMeasureSpec != mOldWidthMeasureSpec ||                 heightMeasureSpec != mOldHeightMeasureSpec) {               // first clears the measured dimension flag             mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;               resolveRtlPropertiesIfNeeded();               int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :                     mMeasureCache.indexOfKey(key);             if (cacheIndex < 0 || sIgnoreMeasureCache) {                 // measure ourselves, this should set the measured dimension flag back                 onMeasure(widthMeasureSpec, heightMeasureSpec); // 注意這個調用,這個方法是本文的重點                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;             } else {                 long value = mMeasureCache.valueAt(cacheIndex);                 // Casting a long to int drops the high 32 bits, no mask needed                 setMeasuredDimension((int) (value >> 32), (int) value);                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;             }               // flag not set, setMeasuredDimension() was not invoked, we raise             // an exception to warn the developer             if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {                 throw new IllegalStateException("onMeasure() did not set the"                         + " measured dimension by calling"                         + " setMeasuredDimension()");             }               mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;         }           mOldWidthMeasureSpec = widthMeasureSpec;         mOldHeightMeasureSpec = heightMeasureSpec;           mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |                 (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension     } 復制代碼 正如方法doc說的那樣,真正的measure過程是發生在onMeasure方法中的,所以你可以也應該override這個方法,我們緊接著看看View中   的默認實現,代碼如下:   復制代碼     /**      * <p>      * Measure the view and its content to determine the measured width and the      * measured height. This method is invoked by {@link #measure(int, int)} and      * should be overriden by subclasses to provide accurate and efficient      * measurement of their contents.      * </p>      *      * <p>      * <strong>CONTRACT:</strong> When overriding this method, you      * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the      * measured width and height of this view. Failure to do so will trigger an      * <code>IllegalStateException</code>, thrown by      * {@link #measure(int, int)}. Calling the superclass'      * {@link #onMeasure(int, int)} is a valid use.      * </p>      *      * <p>      * The base class implementation of measure defaults to the background size,      * unless a larger size is allowed by the MeasureSpec. Subclasses should      * override {@link #onMeasure(int, int)} to provide better measurements of      * their content.      * </p>      *      * <p>      * If this method is overridden, it is the subclass's responsibility to make      * sure the measured height and width are at least the view's minimum height      * and width ({@link #getSuggestedMinimumHeight()} and      * {@link #getSuggestedMinimumWidth()}).      * </p>      *      * @param widthMeasureSpec horizontal space requirements as imposed by the parent.      *                         The requirements are encoded with      *                         {@link android.view.View.MeasureSpec}.      * @param heightMeasureSpec vertical space requirements as imposed by the parent.      *                         The requirements are encoded with      *                         {@link android.view.View.MeasureSpec}.      *      * @see #getMeasuredWidth()      * @see #getMeasuredHeight()      * @see #setMeasuredDimension(int, int)      * @see #getSuggestedMinimumHeight()      * @see #getSuggestedMinimumWidth()      * @see android.view.View.MeasureSpec#getMode(int)      * @see android.view.View.MeasureSpec#getSize(int)      */     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));     }       /**      * <p>This method 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) {         boolean optical = isLayoutModeOptical(this);         if (optical != isLayoutModeOptical(mParent)) {             Insets insets = getOpticalInsets();             int opticalWidth  = insets.left + insets.right;             int opticalHeight = insets.top  + insets.bottom;               measuredWidth  += optical ? opticalWidth  : -opticalWidth;             measuredHeight += optical ? opticalHeight : -opticalHeight;         }         mMeasuredWidth = measuredWidth; // 賦值         mMeasuredHeight = measuredHeight;           mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; // 設置標志位     }       /**      * Returns the suggested minimum height that the view should use. This      * returns the maximum of the view's minimum height      * and the background's minimum height      * ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).      * <p>      * When being used in {@link #onMeasure(int, int)}, the caller should still      * ensure the returned height is within the requirements of the parent.      *      * @return The suggested minimum height of the view.      */     protected int getSuggestedMinimumHeight() {// 找到view的最小大小,沒background的時候返回mMinHeight,這個你可以在xml中指定         return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());                                                // 否則返回mMinHeight和background的最小值裡的較大者     }       /**      * Returns the suggested minimum width that the view should use. This      * returns the maximum of the view's minimum width)      * and the background's minimum width      *  ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).      * <p>      * When being used in {@link #onMeasure(int, int)}, the caller should still      * ensure the returned width is within the requirements of the parent.      *      * @return The suggested minimum width of the view.      */     protected int getSuggestedMinimumWidth() {         return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());     }       /**      * Utility to return a default size. Uses the supplied size if the      * MeasureSpec imposed no constraints. Will get larger if allowed      * by the MeasureSpec.      *      * @param size Default size for this view      * @param measureSpec Constraints imposed by the parent      * @return The size this view should be.      */     public static int getDefaultSize(int size, int measureSpec) {         int result = size;         int specMode = MeasureSpec.getMode(measureSpec);         int specSize = MeasureSpec.getSize(measureSpec);           switch (specMode) {         case MeasureSpec.UNSPECIFIED: // parent imposed的spec沒指定,則用自己的值default size             result = size;             break;         case MeasureSpec.AT_MOST:         case MeasureSpec.EXACTLY: // 否則不論是精確指定或是至多,都用spec提供的值             result = specSize;             break;         }         return result;     } 復制代碼 我們看到View.onMeasure方法只是提供了一個通用的、一般的實現,子類一般需要重載它,自己提供更加合理、高效的實現,最重要的是   符合你的需求。同時我們也看到ViewGroup並沒有提供它自己的實現,但是提供了一些在measure過程中很有用的方法,其特定子類如   FrameLayout、LinearLayout等在measure過程中都需要用到的。     為了更進一步看看這個過程,我們這裡以FrameLayout的onMeasure為例分析下,看其源碼:   復制代碼     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         int count = getChildCount();         // matchparent child標記         final boolean measureMatchParentChildren =                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||                 MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;         mMatchParentChildren.clear(); // 清空列表           int maxHeight = 0;         int maxWidth = 0;         int childState = 0;         // 遍歷children,初步找出maxHeight和maxWidth,順便構造mMatchParentChildren列表         for (int i = 0; i < count; i++) {             final View child = getChildAt(i);             // 默認情況下,只measure非GONE的child,但你可以設置mMeasureAllChildren來打破這一限制             if (mMeasureAllChildren || child.getVisibility() != GONE) {                 // 調用parent,ViewGroup提供的方法。。。                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);                 // 我們平時在xml文件中設置的android:layout_xxx其實就是這裡的LayoutParams                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();                 maxWidth = Math.max(maxWidth,                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);                 maxHeight = Math.max(maxHeight,                         child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);                 childState = combineMeasuredStates(childState, child.getMeasuredState());                 if (measureMatchParentChildren) {                     if (lp.width == LayoutParams.MATCH_PARENT ||                             lp.height == LayoutParams.MATCH_PARENT) {                         mMatchParentChildren.add(child); // 添加matchparent child                     }                 }             }         }         // 進一步調整maxWidth、maxHeight的值,考慮foreground padding、minimum height/width,還有foreground的最小值         // Account for padding too         maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();         maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();           // Check against our minimum height and width         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());           // Check against our foreground's minimum height and width         final Drawable drawable = getForeground();         if (drawable != null) {             maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());             maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());         }         // 設置FrameLayout自身的measuredWidth、measuredHeight         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),                 resolveSizeAndState(maxHeight, heightMeasureSpec,                         childState << MEASURED_HEIGHT_STATE_SHIFT));           count = mMatchParentChildren.size();         if (count > 1) { // 如果有matchparent child的話             for (int i = 0; i < count; i++) {                 final View child = mMatchParentChildren.get(i);                   final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();                 int childWidthMeasureSpec;                 int childHeightMeasureSpec;                   if (lp.width == LayoutParams.MATCH_PARENT) {                     // 如果是MATCH_PARENT的話,由於parent已經measure過了,所以就相當於child指定了確定值,                     // 所以用的MeasureSpec.EXACTLY。。。                     childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -                             getPaddingLeftWithForeground() - getPaddingRightWithForeground() -                             lp.leftMargin - lp.rightMargin,// 在算child大小的時候要去掉parent的padding,child自己指定的各種margin                             MeasureSpec.EXACTLY);                 } else {                     // 否則,根據parent的measureSpec,已經用掉的大小,child的layoutparam的信息,創建一個合適的MeasureSpec                     childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,                             getPaddingLeftWithForeground() + getPaddingRightWithForeground() +                             lp.leftMargin + lp.rightMargin,                             lp.width);                 }                   if (lp.height == LayoutParams.MATCH_PARENT) {                     childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -                             getPaddingTopWithForeground() - getPaddingBottomWithForeground() -                             lp.topMargin - lp.bottomMargin,                             MeasureSpec.EXACTLY);                 } else {                     childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,                             getPaddingTopWithForeground() + getPaddingBottomWithForeground() +                             lp.topMargin + lp.bottomMargin,                             lp.height);                 }                 // 用新的childWidthMeasureSpec、childHeightMeasureSpec再次measure child                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);             }         }     } 復制代碼   接下來,我們重點看看涉及到的幾個ViewGroup方法,代碼如下:   復制代碼     /**      * Ask one of the children of this view to measure itself, taking into      * account both the MeasureSpec requirements for this view and its padding      * and margins. The child must have MarginLayoutParams The heavy lifting is      * done in getChildMeasureSpec.      *      * @param child The child to measure      * @param parentWidthMeasureSpec The width requirements for this view      * @param widthUsed Extra space that has been used up by the parent      *        horizontally (possibly by other children of the parent)      * @param parentHeightMeasureSpec The height requirements for this view      * @param heightUsed Extra space that has been used up by the parent      *        vertically (possibly by other children of the parent)      */     protected void measureChildWithMargins(View child,             int parentWidthMeasureSpec, int widthUsed,             int parentHeightMeasureSpec, int heightUsed) {         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();           final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin // 考慮上parent的padding和child的margin                         + widthUsed, lp.width);         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin                         + heightUsed, lp.height);         // 這裡如果child是個ViewGroup類型,則實際會遞歸下去。。。         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);     }       /**      * Does the hard part of measureChildren: figuring out the MeasureSpec to      * pass to a particular child. This method figures out the right MeasureSpec      * for one dimension (height or width) of one child view.      *      * The goal is to combine information from our MeasureSpec with the      * LayoutParams of the child to get the best possible results. For example,      * if the this view knows its size (because its MeasureSpec has a mode of      * EXACTLY), and the child has indicated in its LayoutParams that it wants      * to be the same size as the parent, the parent should ask the child to      * layout given an exact size.      *      * @param spec The requirements for this view      * @param padding The padding of this view for the current dimension and      *        margins, if applicable      * @param childDimension How big the child wants to be in the current      *        dimension      * @return a MeasureSpec integer for the child      */      public static int getChildMeasureSpec(int spec, int padding, int childDimension) {         // 這個方法是協商型的,最終結果既可能直接由spec(parent提供的),也可能由childDimension決定         // 所以我們知道了,一個View的大小不是簡單的單方面決定的,而是通過一系列條件協商的結果,         // 有時會尊重parent的spec,有時會堅持自己的dimension要求         int specMode = MeasureSpec.getMode(spec);         int specSize = MeasureSpec.getSize(spec);           int size = Math.max(0, specSize - padding); // 可用的大小           int resultSize = 0;         int resultMode = 0;           switch (specMode) {         // Parent has imposed an exact size on us         case MeasureSpec.EXACTLY: // parent說child你應該是個確定的大小             if (childDimension >= 0) { // child正好設置了確定的大小                 resultSize = childDimension; // 讓child是那個確定的大小                 resultMode = MeasureSpec.EXACTLY; // 設置mode為EXACTLY             } else if (childDimension == LayoutParams.MATCH_PARENT) {                 // Child wants to be our size. So be it.                 resultSize = size; // 其他情況下都是parent spec中的大小,只是mode不同                 resultMode = MeasureSpec.EXACTLY;             } else if (childDimension == LayoutParams.WRAP_CONTENT) {                 // Child wants to determine its own size. It can't be                 // bigger than us.                 resultSize = size; // 不能超過size                 resultMode = MeasureSpec.AT_MOST;             }             break;           // Parent has imposed a maximum size on us         case MeasureSpec.AT_MOST: // parent說child你應該最大是某個值。。。             if (childDimension >= 0) { // child指定確定值了,則聽child的                 // Child wants a specific size... so be it                 resultSize = childDimension;                 resultMode = MeasureSpec.EXACTLY;             } else if (childDimension == LayoutParams.MATCH_PARENT) {                 // Child wants to be our size, but our size is not fixed.                 // Constrain child to not be bigger than us.                 resultSize = size;                 resultMode = MeasureSpec.AT_MOST;             } else if (childDimension == LayoutParams.WRAP_CONTENT) {                 // Child wants to determine its own size. It can't be                 // bigger than us.                 resultSize = size;                 resultMode = MeasureSpec.AT_MOST;             }             break;           // Parent asked to see how big we want to be         case MeasureSpec.UNSPECIFIED: // parent沒對child的大小有啥要求             if (childDimension >= 0) { // child指定了確定的值,聽child的                 // Child wants a specific size... let him have it                 resultSize = childDimension;                 resultMode = MeasureSpec.EXACTLY;             } else if (childDimension == LayoutParams.MATCH_PARENT) {                 // Child wants to be our size... find out how big it should                 // be                 resultSize = 0;                 resultMode = MeasureSpec.UNSPECIFIED;             } else if (childDimension == LayoutParams.WRAP_CONTENT) {                 // Child wants to determine its own size.... find out how                 // big it should be                 resultSize = 0;                 resultMode = MeasureSpec.UNSPECIFIED;             }             break;         }         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);     }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved