Android初始化的時候擷取載入的布局的寬高(續)--RelativeLayout的陷阱

來源:互聯網
上載者:User

Android初始化的時候擷取載入的布局的寬高(續)--RelativeLayout的陷阱

接著上次的問題,已經介紹過,在初始化或者說OnCreate方法中擷取載入的布局的寬高,最後說到,調用view.measure(0, 0);然後在調用getMeasuredWidth和getMeasuredHeight就可以獲得測量的寬高。可以參考:Android如何在初始化的時候擷取載入的布局的寬高
今天在寫類似的效果時,給ListView載入一個頭部視圖,通過listView$addHeadView添加到ListView的頭部。為了描述起來簡單起見,這個頭部視圖的布局我做了簡化為下面:headview.xml

    

然後在初始化的時候想擷取該布局的寬高,就這樣做了:

//布局載入器private LayoutInflater inflater;//頭部的Viewprivate View headView;//頭部View的寬private int headViewWidth;//頭部View的高private int headViewHeight;inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);headView = inflater.inflate(R.layout.headview, null);headView.measure(0, 0);headViewWidth = headView.getMeasuredWidth();headViewHeight = headView.getMeasuredHeight();Log.i(TAG, "headViewWidth-->" + headViewWidth + "  headViewHeight-->" + headViewHeight);addHeaderView(headView);

結果。。。。。

程式崩潰了!尼瑪,不是說好的headView.measure(0, 0);調用完就能擷取到布局的寬高了嘛,怎麼這句好報錯null 指標異常了?

然後把布局修改成線性布局

    

竟然就好了。。。


也擷取到了測量的寬高:


接下來又開始了對問題的研究。為了簡化問題,還是按照上次的此路。在上次的基礎上添加布局viewgroup_relativelayout.xml,其實就是將外層的LinearLayout修改成RelativeLayout了,其他地方未變。載入該布局,然後測量寬高。

        

看結果:


View,textview和線性布局的ViewGroup和上次的結果一樣。但是將相對布局的ViewGroup直接掛掉。。剛開始還是特別的不爽的,為什麼經常遇到問題,不過一想,解決問題的過程就是水平提高的過程,所以打起精神進行研究。嘿嘿~

通過比較發現RelativeLayout出錯了,所以很容易將null 指標異常的地方定位在RelativeLayout的onMeasure函數中。

但是這個函數比較複雜了。兩百多行代碼,裡面牽扯了很多的域變數和函數,讀起來挺累的。因此在網上搜了好久,在eoe上面看到有人說了下面的方法:

如果使用Inflater的情況下會出現以上錯誤,原因是用Inflater渲染組件的時候並沒有給其指定父控制項,所以渲染器不會去解析width 和 height屬性,就會導致null 指標異常。

解決方案:

view.setLayoutParams(newLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));指定寬度和高度,然後調用measure方法就OK。

其實這個說法前面就研究過,確實是這樣子的,但是LinearLayout為什麼沒出問題RelativeLayout就出問題了呢,所以還是沒有說明白原因在哪裡。

我嘗試了一下確實搞定了,原因尚不清楚,那就肯定需要查原始碼了。在這個指導下給我提供了方向就是很可能在onMeasure方法裡面調用了該相對布局的參數mLayoutParams,由於本來為null,那麼調用的話就會報告null 指標異常!

接下來就在源碼的onMeasure函數裡面將所有出現mLayoutParams的地方打上斷點。然後斷點調試

RelativeLayou類(我查看的是4.2的源碼)

//493行if (mLayoutParams.width >= 0) {                width = Math.max(width, mLayoutParams.width);            }//523行if (mLayoutParams.height >= 0) {                height = Math.max(height, mLayoutParams.height);            }

果然mLayoutParams變數不存在為null。

到現在為止問題就清楚了。由於調用headView.measure(0, 0);的時候是通過inflate(R.layout.headview, null);方式載入的布局,因此設定的外層RelativeLayout布局的LayoutParams是null的,恰巧相對布局沒有檢查是否為null就直接調用了mLayoutParams.width或者mLayoutParams.height,因此就報null 指標錯誤了。這可能是一個bug吧我認為。

既然知道了問題是出在了這裡了,那麼也很容易解決了。

至少有以下的解決方案:

1,載入布局的時候通過inflater.inflate(R.layout.viewgroup_relativelayout,(ViewGroup) findViewById(R.id.mainLayout),false);方式載入。這樣的話會設定他的布局參數。

2 手動設定布局參數添加上

lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);setLayoutParams(lp);


現在再來看上一篇部落格裡面講的這個方法:

private void measureView(View child) {ViewGroup.LayoutParams lp = child.getLayoutParams();if(lp == null){lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);//child.setLayoutParams(lp);}//headerView的寬度資訊int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);int childMeasureHeight;if(lp.height > 0){childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);} else {childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);//未指定}//將寬和高設定給childchild.measure(childMeasureWidth, childMeasureHeight);}

看起來貌似就是為了避免外層布局是RelativeLayout的情況對LayoutParams做了判斷!但是直接調用上面這個方法仍然不行,因為new出來的LayoutParams沒有設定進去,所以還需要調用child.setLayoutParams(lp);然後就搞定了。經過測試ok!



回到部落格開始在ListView添加頭View所遇到的問題也就很容易解決了。

1,將相對布局換成線性布局。

2,仍然使用相對布局,使用上面說的兩種方法設定外層的相對布局的布局參數。比如使用下面的方法,在家在布局的時候帶上父布局,結果搞定!好開心

inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);headView = inflater.inflate(R.layout.headview, this,false);headView.measure(0, 0);//換成方法measureView (headView)也可以headViewWidth = headView.getMeasuredWidth();headViewHeight = headView.getMeasuredHeight();Log.i(TAG, "headViewWidth-->" + headViewWidth + "  headViewHeight-->" + headViewHeight);addHeaderView(headView);

將headView.measure(0, 0);換成measureView (headView);也可以的。

但是載入布局的時候不帶上父布局,調用的是headView =inflater.inflate(R.layout.headview, null);即使用下面代碼:

inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);headView = inflater.inflate(R.layout.headview, null);//headView.measure(0, 0);measureView(headView);headViewWidth = headView.getMeasuredWidth();headViewHeight = headView.getMeasuredHeight();Log.i(TAG, "headViewWidth-->" + headViewWidth + "  headViewHeight-->" + headViewHeight);addHeaderView(headView);

程式竟然又崩潰了。看調試結果:


不過不用擔心,這時的錯誤不是null 指標了,而是類轉換異常,相信通過錯誤很容易修改了

lp= new AbsListView.LayoutParams(AbsListView.LayoutParams.FILL_PARENT,AbsListView.LayoutParams.WRAP_CONTENT);

然後在運行就好了,大功告成。最後的忠告就是需要自己需要測量布局寬高的時候慎用RelativeLayout,如同解決ScrollView和ListView衝突的時候一種解決方案,這裡我們只討論其中一個衝突就是:導致ListView的高度顯示不完整,出現只顯示部分內容的情況。解決方案就是就是測量ListView的總高度,然後設定一下,關鍵代碼如下。試想,如果這時候如果各個item是RelativeLayout的話,肯定會出錯。

private void setListViewHeight(ListView listView) {           ListAdapter listAdapter = listView.getAdapter();            if (listAdapter == null) {               return;           }           int totalHeight = 0;           for (int i = 0; i < listAdapter.getCount(); i++) {               View listItem = listAdapter.getView(i, null, listView);               listItem.measure(0, 0);               totalHeight += listItem.getMeasuredHeight();           }           ViewGroup.LayoutParams params = listView.getLayoutParams();           params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));           listView.setLayoutParams(params);      }  

最後,這些東西可能有些人寫程式的時候都從沒遇到過,當然也不影響寫出漂亮的APP。但是我認為不管是什麼問題,都能鍛煉解決問題的能力,都是有助於學習。

文字表達能力太差,表述能力是硬傷啊!囧…剛開始寫部落格,感謝大家圍觀。



測試載入各種類型布局的寬高參數的Demo和ListView添加頭視圖為相對布局的Demo

點此下載代碼








聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.