android 1.6中LinearLayout getBaseline函數的一個bug

在項目中遇到一個bug,程序在android1.6上直接crash,可是在其餘版本上均正常,錯誤日誌以下:html

04-07 17:02:53.512: E/AndroidRuntime(360): java.lang.RuntimeException: mBaselineAlignedChildIndex of LinearLayout set to an index that is out of bounds.
04-07 17:02:53.512: E/AndroidRuntime(360): 	at android.widget.LinearLayout.getBaseline(LinearLayout.java:151)
04-07 17:02:53.512: E/AndroidRuntime(360): 	at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:644)
04-07 17:02:53.512: E/AndroidRuntime(360): 	at android.widget.LinearLayout.onMeasure(LinearLayout.java:280)
04-07 17:02:53.512: E/AndroidRuntime(360): 	at android.view.View.measure(View.java:7703)

分析:這個錯誤意思是說,在LinearLayout中,measure函數裏,當我要對其child進行基線對齊到第一個child的時候,發現個人內部沒有child,結果基線對齊到第一個child(index爲0)的時候,因爲沒法取到第一個child的信息,因此數組越界了。我當時在網上調研了下,發現有人說這個android1.x的一個bug,mBaselineAlignedChildIndex爲LinearLayout的一個私有成員變量,在android1.x默認值是0,1.x以上默認值是-1,在google code上甚至有人舉報了這個bug。這也就解釋了爲何程序跑在1.6上直接crash,而在2.x上卻正常。java

咱們看一下2.x上這個getBaseline函數android

@Override
    public int getBaseline() {
        if (mBaselineAlignedChildIndex < 0) {
            return super.getBaseline();
        }

        if (getChildCount() <= mBaselineAlignedChildIndex) {
            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
                    + "set to an index that is out of bounds.");
        }
上述邏輯代表,若是mBaselineAlignedChildIndex = -1的話,直接return了,不然若是child數 <= mBaselineAlignedChildIndex,則訪問越界,也就是文章開始提到的異常。從這段代碼能夠看出,當mBaselineAlignedChildIndex = -1的狀況,在android2.x是不可能出現上述異常的,當時android1.6上mBaselineAlignedChildIndex居然等於0,結果直接拋出異常了。


解決方法:api

1.  就我目前在網上的調研,有人建議出現此類問題的時候,先addView再removeView,這樣或許能解決問題,可是我以爲這樣作很怪,讓人以爲不可理解,無緣無故的add一個又remove掉了,若是不寫註釋的話,別人可能會以爲代碼寫的有誤。數組

2.  放棄了第一種方法,只好另外想辦法,後來在android源碼中找到了思路。請看LinearLayout中的measureHorizontal函數,其中有一段以下ide

if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
                // Optimization: don't bother measuring children who are going to use
                // leftover space. These views will get measured again down below if
                // there is any leftover space.
                if (isExactly) {
                    mTotalLength += lp.leftMargin + lp.rightMargin;
                } else {
                    final int totalLength = mTotalLength;
                    mTotalLength = Math.max(totalLength, totalLength +
                            lp.leftMargin + lp.rightMargin);
                }

                // Baseline alignment requires to measure widgets to obtain the
                // baseline offset (in particular for TextViews). The following
                // defeats the optimization mentioned above. Allow the child to
                // use as much space as it wants because we can shrink things
                // later (and re-measure).
 if (baselineAligned) { final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); child.measure(freeSpec, freeSpec); }
            }

對比出錯的log,發現調用棧很一致,OnMeasure調用了measureHorizontal,measureHorizontal調用了child的measure。注意到上述代碼的最後幾句,if(baselineAligned)這一句很關鍵,這個變量就表示是否要進行基線對齊,其實這個基線對齊對佈局的影響不大,通常來講咱們不須要設置這個變量,我想既然是基線對齊的時候程序掛的,那爲什麼不取消基線對齊呢,恰好LinearLayout提供了setBaselineAligned這個函數,試着調用setBaselineAligned(false),發現問題解決了。函數

整理一下,判斷一下,若是發現系統版本是1.x(基本只有1.6和1.5),則調用setBaselineAligned(false),其餘api版本什麼也不作。佈局

int mVersionCode = 8;//default value = android 2.2
		try
		{
			mVersionCode = Integer.valueOf(android.os.Build.VERSION.SDK);
			Log.d(TAG, "sdk version=" + mVersionCode);
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		//針對android1.6及如下的特殊處理 此爲android的低版本bug
		if(mVersionCode <= 5)
		{
			linearLayoutScrolLayout.setBaselineAligned(false);
		}
相關文章
相關標籤/搜索