一、引入
你是不是经常遇到在fragment中调用findViewById方法寻找fragment布局文件中的控件返回null的现象。我之前也遇到了这个问题,虽然后来解决了,但是心中一直有疑惑,最近有时间停下来,结合别人的解答和自己的思考,对这个问题进行彻底的梳理。
二、使用getActivity().findViewById
1、getActivity的介绍
activity中的控件们(findViewById()))。当fragment生命周期结束并销毁时,getActivity()返回的会是null。onAttach和onDetach之间的其他生命周期方法都可以调用getActivity方法。
2、在Fragment的oncreateview()方法中使用
1)代码:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//从数据库中得到黑名单的集合
super<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.onActivityCreated</span>(savedInstanceState)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
View view = inflater<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inflate</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.layout</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.fragment</span>_dark, container, false)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
infoList = DbManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getInstance</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDarkList</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView = (ListView) getActivity()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findViewById</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.id</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dark</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
adapter = new DarkAdapter(getActivity(), infoList)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setAdapter</span>(adapter)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
registerForContextMenu(listView)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
return view<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
2)结果:
空指针异常
3)分析:
因为onCreateView时,fragment已经和activity绑定了,所以说getActivity是有值的,但是我们返回值为空,说明在activity的子控件中找不到fragment的控件——>说明fragment的控件还没有加到activity中。
为什么呢?原因如下:
View view = inflater.inflate(R.layout.fragment_dark, container, false);
该方法的第二个参数的意思是:root Optional view to be the parent of the generated hierarchy (if attachToRoot is true), or else simply an object that provides a set of LayoutParams values for root of the returned hierarchy (if attachToRoot is false.)
该方法的第三个参数的意思是:Whether the inflated hierarchy should be attached to the root parameter? If false, root is only used to create the correct subclass of LayoutParams for the root view in the XML.
在本代码中第三个参数为false,说明当前fragment的布局文件并没有被加到activity的布局中,所以你当然无法在activity中找到fragment的组件了。但是一旦onCreateView执行完了,系统就把fragment的布局文件加入到activity的布局文件中了。所以只要在onCreateView方法之后,我们通过getActivity().findViewById都是可以得到fragment的子控件的。所以下面的3结果就是对的。
有的人可能有疑问了:如果我们把false改为true,这样是不是就可以在onCreateView使用getActivity().findViewById找到fragment的子控件了。事实上,这样做会产生异常,你把fragment的布局文件加到activity的布局一次,但是当onCreateView方法执行完之后,返回View,系统又执行了一次把fragment的布局文件加到activity的布局,就会产生错误。
3、在Fragment的onstart()方法中使用
1)代码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onStart</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onStart();
infoList = DbManager.getInstance().getDarkList();
listView = (ListView) getActivity().findViewById(R.id.dark);
adapter = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DarkAdapter(getActivity(), infoList);
listView.setAdapter(adapter);
registerForContextMenu(listView);
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
2)结果:
正常
3)分析:
理解了2的分析,这里就没有问题了。
三、使用getview().findViewById
1、getview的介绍
开发文档的解释:
the one returned by onCreateView(LayoutInflater, ViewGroup, Bundle)), if provided.
Returns
The fragment’s root view, or null if it has no layout.
2、在Fragment的oncreateview()方法中使用
1)代码:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//从数据库中得到黑名单的集合
super<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.onActivityCreated</span>(savedInstanceState)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
View view = inflater<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inflate</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.layout</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.fragment</span>_dark, container, false)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
infoList = DbManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getInstance</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDarkList</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView = (ListView) getView()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findViewById</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.id</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dark</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
adapter = new DarkAdapter(getActivity(), infoList)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setAdapter</span>(adapter)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
registerForContextMenu(listView)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
return view<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
2)效果:
空指针异常
3)分析:
the one returned by onCreateView(LayoutInflater, ViewGroup, Bundle)), if provided.而现在,我们在onCreateView内部使用getView,onCreateView还没有执行完,getView自然无法得到onCreateView的返回值。因此产生空指针异常
3、在Fragment的onstart()方法中使用
1)代码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onStart</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onStart();
infoList = DbManager.getInstance().getDarkList();
listView = (ListView) getView().findViewById(R.id.dark);
adapter = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DarkAdapter(getActivity(), infoList);
listView.setAdapter(adapter);
registerForContextMenu(listView);
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
2)结果:
空指针异常
3)分析:
易理解
四、推荐的方式
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//从数据库中得到黑名单的集合
super<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.onActivityCreated</span>(savedInstanceState)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
View view = inflater<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.inflate</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.layout</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.fragment</span>_dark, container, false)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
infoList = DbManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getInstance</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getDarkList</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView = (ListView) view<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findViewById</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.id</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.dark</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
adapter = new DarkAdapter(getActivity(), infoList)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
listView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setAdapter</span>(adapter)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
registerForContextMenu(listView)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
return view<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
五、更多知识的讲解
1、findViewById
findViewById()是View对象的方法,先通过inflate()方法得到View,调用这个View对象的getViewById()方法,就能得到这个View树上的子View。
findViewById的完整写法是View.findViewById(),而不指定View时默认的是Context,因此当findViewById不是在context里执行时,要指定对应的View
例如:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">userDialog=new Dialog(addevent<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.this</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
userDialog<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setContentView</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.layout</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.user</span>_list)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
userDialog<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setTitle</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"请选择"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
ListView lv=(ListView)userDialog<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.findViewById</span>(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.id</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.userList</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
lv<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setAdapter</span>(new MyAdapter())<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">; </span>
userDialog<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.show</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
如上,实例化lv时必须指定userDialog.findViewById()而不能直接findViewById(),否则就会从Activity而不是Dialog的布局文件中找R.id.userList,此时当然会返回null,执行lv.setAdapter(new MyAdapter());时就会出现NullPointException异常。
findViewById返回为null,原因是:find的View下面,没有包含对应的想要找的view,从而导致找不到,返回null。
常见的findViewById返回null的问题:在setContentView调用之前,调用了findViewById去找main布局中的界面元素lv_contactbook,那么所得到的lv一定是null。正确的做法是将上面代码中加粗的哪一行,挪至setContentView方法调用之后。