- 典型重現環境
機型: Sony Ericsson
Android version: 2.3.4
StackTrace:
驗證代碼:E/AndroidRuntime( 3579): FATAL EXCEPTION: main E/AndroidRuntime( 3579): java.lang.NullPointerException E/AndroidRuntime( 3579): at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:431) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at com.example.measureverify.MainActivity$MenuListAdapter.getView(MainActivity.java:85) E/AndroidRuntime( 3579): at android.widget.AbsListView.obtainView(AbsListView.java:1519) E/AndroidRuntime( 3579): at android.widget.ListView.measureHeightOfChildren(ListView.java:1220) E/AndroidRuntime( 3579): at android.widget.ListView.onMeasure(ListView.java:1131) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:581) E/AndroidRuntime( 3579): at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:365) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3231) E/AndroidRuntime( 3579): at android.widget.FrameLayout.onMeasure(FrameLayout.java:254) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at android.widget.LinearLayout.measureVertical(LinearLayout.java:535) E/AndroidRuntime( 3579): at android.widget.LinearLayout.onMeasure(LinearLayout.java:313) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3231) E/AndroidRuntime( 3579): at android.widget.FrameLayout.onMeasure(FrameLayout.java:254) E/AndroidRuntime( 3579): at android.view.View.measure(View.java:8462) E/AndroidRuntime( 3579): at android.view.ViewRoot.performTraversals(ViewRoot.java:861) E/AndroidRuntime( 3579): at android.view.ViewRoot.handleMessage(ViewRoot.java:1882) E/AndroidRuntime( 3579): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime( 3579): at android.os.Looper.loop(Looper.java:130) E/AndroidRuntime( 3579): at android.app.ActivityThread.main(ActivityThread.java:3701) E/AndroidRuntime( 3579): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime( 3579): at java.lang.reflect.Method.invoke(Method.java:507) E/AndroidRuntime( 3579): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) E/AndroidRuntime( 3579): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:624) E/AndroidRuntime( 3579): at dalvik.system.NativeStart.main(Native Method) W/ActivityManager( 237): Force finishing activity com.example.measureverify/.MainActivity
public class MenuListAdapter extends ArrayAdapter<ListItem> { private Activity context; public MenuListAdapter(Activity context) { super(context, 0); this.context = context; } public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { if (unsafetyInflate) { // risk inflate case convertView = LayoutInflater.from(context).inflate( R.layout.custom_infowindow, null); } else { // safety inflate case(### Solution 1 ###) // The second parameter "mListView" provides a set of // LayoutParams values for root of the returned hierarchy convertView = LayoutInflater.from(context).inflate( R.layout.custom_infowindow, mListView, false); } // ### Solution 2 ### if (MainActivity.this.setLayoutParamsProgrammatically) { // NOTE: the layout params set here should be of the // {ParentView}.LayoutParams convertView .setLayoutParams(new ListView.LayoutParams( ListView.LayoutParams.WRAP_CONTENT, ListView.LayoutParams.WRAP_CONTENT)); } Log.d(TAG, "case 1 parent:" + convertView.getParent() + " layoutParams:" + convertView.getLayoutParams()); final int width = context.getWindow().getDecorView().getWidth(); final int height = context.getWindow().getDecorView().getHeight(); convertView.measure(width, height); MainActivity.this.mLayout = convertView; } final ListItem item = (ListItem) getItem(position); TextView title = (TextView) convertView.findViewById(R.id.title); title.setText("title " + Math.random() + item.prop_1); TextView snippet = (TextView) convertView.findViewById(R.id.snippet); snippet.setText("snippet " + Math.random() + item.prop_2); return convertView; } }
- 相關Android2.3.6 Framework層代碼
View.java中有一成員代表layout參數(子View對父View的請求,或者一種期望值,最終由整個View層次樹決定)
以及獲取該成員的函數:/** 1623 * The layout parameters associated with this view and used by the parent 1624 * {@link android.view.ViewGroup} to determine how this view should be 1625 * laid out. 1626 * {@hide} 1627 */ 1628 protected ViewGroup.LayoutParams mLayoutParams;
4973 /** 4974 * Get the LayoutParams associated with this view. All views should have 4975 * layout parameters. These supply parameters to the <i>parent</i> of this 4976 * view specifying how it should be arranged. There are many subclasses of 4977 * ViewGroup.LayoutParams, and these correspond to the different subclasses 4978 * of ViewGroup that are responsible for arranging their children. 4979 * @return The LayoutParams associated with this view 4980 */ 4981 @ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_") 4982 public ViewGroup.LayoutParams getLayoutParams() { 4983 return mLayoutParams; 4984 }
ViewGroup類中有一個方法用於將detached的View attach到父View:2352 * This method should be called only for view which were detached from their parent. 2353 * 2354 * @param child the child to attach 2355 * @param index the index at which the child should be attached 2356 * @param params the layout parameters of the child 2357 * 2358 * @see #removeDetachedView(View, boolean) 2359 * @see #detachAllViewsFromParent() 2360 * @see #detachViewFromParent(View) 2361 * @see #detachViewFromParent(int) 2362 */ 2363 protected void attachViewToParent(View child, int index, LayoutParams params) { 2364 child.mLayoutParams = params; <span style="margin: 0px; padding: 0px; color: rgb(255, 102, 102);">// 編者注:此處子View的<span style="margin: 0px; padding: 0px; font-family: Arial, Helvetica, sans-serif;">mLayoutParams被設置</span></span> 2365 2366 if (index < 0) { 2367 index = mChildrenCount; 2368 } 2369 2370 addInArray(child, index); 2371 2372 child.mParent = this; 2373 child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN; 2374 2375 if (child.hasFocus()) { 2376 requestChildFocus(child, child.findFocus()); 2377 } 2378 }
在ListView/GridView中都用調用此方法來設置好子View。
ViewGroup類中同時還有另外一個更主流的方法(整個Layout被從xml中鋪陳開attach到Window並變得有活力的過程中該方法會被調用到)
由以上代碼片斷綜合可知,在一個View僅僅從XML中Inflate出來未被attach到View層次樹裏邊去的時候,View.mLayoutParams成員爲空。private void addViewInner(View child, int index, LayoutParams params, 1973 boolean preventRequestLayout) { 1974 1975 if (child.getParent() != null) { 1976 throw new IllegalStateException("The specified child already has a parent. " + 1977 "You must call removeView() on the child's parent first."); 1978 } 1979 1980 if (!checkLayoutParams(params)) { 1981 params = generateLayoutParams(params); 1982 } 1983 1984 if (preventRequestLayout) { 1985 child.mLayoutParams = params; <span style="margin: 0px; padding: 0px; color: rgb(255, 102, 102); font-family: Arial, Helvetica, sans-serif;">// 編者注:直接不理會子View請求/意願的case,直接由父View分配,強迫子View接受</span> 1986 } else { 1987 child.setLayoutParams(params); <span style="margin: 0px; padding: 0px; color: rgb(255, 102, 102); font-family: Arial, Helvetica, sans-serif;">// 編者注:溫柔一刀的做法,非常體諒的設置給子View,到底滿意不滿意,取決於子View自身</span> 1988 } 1989 1990 if (index < 0) { 1991 index = mChildrenCount; 1992 } 1993 1994 addInArray(child, index); 1995 1996 // tell our children 1997 if (preventRequestLayout) { 1998 child.assignParent(this); 1999 } else { 2000 child.mParent = this; 2001 } 2002 2003 if (child.hasFocus()) { 2004 requestChildFocus(child, child.findFocus()); 2005 } 2006 2007 AttachInfo ai = mAttachInfo; 2008 if (ai != null) { 2009 boolean lastKeepOn = ai.mKeepScreenOn; 2010 ai.mKeepScreenOn = false; 2011 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 2012 if (ai.mKeepScreenOn) { 2013 needGlobalAttributesUpdate(true); 2014 } 2015 ai.mKeepScreenOn = lastKeepOn; 2016 } 2017 2018 if (mOnHierarchyChangeListener != null) { 2019 mOnHierarchyChangeListener.onChildViewAdded(this, child); 2020 } 2021 2022 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 2023 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 2024 } 2025 } 2026
-
RelativeLayout爲什麼會NullPointerException在onMeasure 回到文章開頭的StackTrace,同時參照RelativeLayout的onMeasure():
不難看出,在RelativeLayout被add/attach到父View之前mLayoutParams成員爲空,調用measure方法將導致上圖標註處代碼拋出空指針異常。426 if (isWrapContentWidth) { 427 // Width already has left padding in it since it was calculated by looking at 428 // the right of each child view 429 width += mPaddingRight; 430 431 if (mLayoutParams.width >= 0) { <span style="margin: 0px; padding: 0px; color: rgb(255, 102, 102); font-family: Arial, Helvetica, sans-serif;">// 編者注:該處爲onMeasure方法中第一次使用到mLayoutParams的地方</span> 432 width = Math.max(width, mLayoutParams.width); 433 } 434 435 width = Math.max(width, getSuggestedMinimumWidth()); 436 width = resolveSize(width, widthMeasureSpec); 437 438 if (offsetHorizontalAxis) { 439 for (int i = 0; i < count; i++) { 440 View child = getChildAt(i); 441 if (child.getVisibility() != GONE) { 442 LayoutParams params = (LayoutParams) child.getLayoutParams();
-
解決方案有兩種 a) 在measure之前顯式設置LayoutParams(代表着對父View的Layout請求,必須是父View的內部LayoutParams類型) b) 自動設置LayoutParams的inflate方式 請見代碼:
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { if (unsafetyInflate) { // risk inflate case convertView = LayoutInflater.from(context).inflate( R.layout.custom_infowindow, null); } else { // safety inflate case(### Solution 1 ###) // The second parameter "mListView" provides a set of // LayoutParams values for root of the returned hierarchy convertView = LayoutInflater.from(context).inflate( R.layout.custom_infowindow, mListView, false); } // ### Solution 2 ### if (MainActivity.this.setLayoutParamsProgrammatically) { // NOTE: the layout params set here should be of the // {ParentView}.LayoutParams convertView .setLayoutParams(new ListView.LayoutParams( ListView.LayoutParams.WRAP_CONTENT, ListView.LayoutParams.WRAP_CONTENT)); } Log.d(TAG, "case 1 parent:" + convertView.getParent() + " layoutParams:" + convertView.getLayoutParams()); final int width = context.getWindow().getDecorView().getWidth(); final int height = context.getWindow().getDecorView().getHeight(); convertView.measure(width, height); MainActivity.this.mLayout = convertView; } final ListItem item = (ListItem) getItem(position); TextView title = (TextView) convertView.findViewById(R.id.title); title.setText("title " + Math.random() + item.prop_1); TextView snippet = (TextView) convertView.findViewById(R.id.snippet); snippet.setText("snippet " + Math.random() + item.prop_2); return convertView; }
完整工程代碼截圖:
-
Android各個平臺上這一問題的可復現性 在查看多個版本Android源碼後發現,Android 4.4_r1中已經針對該NullPointerException做了防範:
539 if (mLayoutParams != null && mLayoutParams.width >= 0) { 540 width = Math.max(width, mLayoutParams.width); 541 } 542 543 width = Math.max(width, getSuggestedMinimumWidth()); 544 width = resolveSize(width, widthMeasureSpec); 545 546 if (offsetHorizontalAxis) { 547 for (int i = 0; i < count; i++) { 548 View child = getChildAt(i); 549 if (child.getVisibility() != GONE) { 550 LayoutParams params = (LayoutParams) child.getLayoutParams(); 551 final int[] rules = params.getRules(layoutDirection); 552 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { 553 centerHorizontal(child, params, width); 554 } else if (rules[ALIGN_PARENT_RIGHT] != 0) { 555 final int childWidth = child.getMeasuredWidth(); 556 params.mLeft = width - mPaddingRight - childWidth; 557 params.mRight = params.mLeft + childWidth; 558 } 559 } 560 } 561 } 562 }
<span style="font-family: Arial; background-color: rgb(255, 255, 255);">轉自:</span><span style="font-family: Arial; background-color: rgb(255, 255, 255);">http://blog.csdn.net/wangfei584521/article/details/25987377</span>
調用 RelativeLayout measure()報 java.lang.NullPointerException
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.