【Fragment精深系列5】fragment findViewById()返回null完全解析

一、引入

  你是不是经常遇到在fragment中调用findViewById方法寻找fragment布局文件中的控件返回null的现象。我之前也遇到了这个问题,虽然后来解决了,但是心中一直有疑惑,最近有时间停下来,结合别人的解答和自己的思考,对这个问题进行彻底的梳理。

二、使用getActivity().findViewById

1、getActivity的介绍

  Fragment中有一个getActivity()的方法。返回与Fragment关联的Activity对象(通过该对象可以查找activity中的控件们(findViewById()))。当fragment生命周期结束并销毁时,getActivity()返回的会是null。onAttach和onDetach之间的其他生命周期方法都可以调用getActivity方法。

2、在Fragment的oncreateview()方法中使用

1)代码:
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //从数据库中得到黑名单的集合
        super.onActivityCreated(savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_dark, container, false);
        infoList = DbManager.getInstance().getDarkList();
        listView = (ListView) getActivity().findViewById(R.id.dark);
        adapter = new DarkAdapter(getActivity(), infoList);
        listView.setAdapter(adapter);
        registerForContextMenu(listView);
        return view;
    }
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)代码:
    @Override
    public void onStart() {
        super.onStart();
        infoList = DbManager.getInstance().getDarkList();
        listView = (ListView) getActivity().findViewById(R.id.dark);
        adapter = new DarkAdapter(getActivity(), infoList);
        listView.setAdapter(adapter);
        registerForContextMenu(listView);
    }
2)结果:

正常

3)分析:

理解了2的分析,这里就没有问题了。

三、使用getview().findViewById

1、getview的介绍

开发文档的解释:

Get the root view for the fragment’s layout (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)代码:
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //从数据库中得到黑名单的集合
        super.onActivityCreated(savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_dark, container, false);
        infoList = DbManager.getInstance().getDarkList();
        listView = (ListView) getView().findViewById(R.id.dark);
        adapter = new DarkAdapter(getActivity(), infoList);
        listView.setAdapter(adapter);
        registerForContextMenu(listView);
        return view;
    }
2)效果:

空指针异常

3)分析:

  我们看getView的方法解释:Get the root view for the fragment’s layout (the one returned by onCreateView(LayoutInflater, ViewGroup, Bundle)), if provided.而现在,我们在onCreateView内部使用getView,onCreateView还没有执行完,getView自然无法得到onCreateView的返回值。因此产生空指针异常

3、在Fragment的onstart()方法中使用

1)代码:
    @Override
    public void onStart() {
        super.onStart();
        infoList = DbManager.getInstance().getDarkList();
        listView = (ListView) getView().findViewById(R.id.dark);
        adapter = new DarkAdapter(getActivity(), infoList);
        listView.setAdapter(adapter);
        registerForContextMenu(listView);
    }
2)结果:

空指针异常

3)分析:

易理解

四、推荐的方式

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //从数据库中得到黑名单的集合
        super.onActivityCreated(savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_dark, container, false);
        infoList = DbManager.getInstance().getDarkList();
        listView = (ListView) view.findViewById(R.id.dark);
        adapter = new DarkAdapter(getActivity(), infoList);
        listView.setAdapter(adapter);
        registerForContextMenu(listView);
        return view;
    }

五、更多知识的讲解

1、findViewById

  findViewById()是View对象的方法,先通过inflate()方法得到View,调用这个View对象的getViewById()方法,就能得到这个View树上的子View。

  findViewById的完整写法是View.findViewById(),而不指定View时默认的是Context,因此当findViewById不是在context里执行时,要指定对应的View
例如:

userDialog=new Dialog(addevent.this);
userDialog.setContentView(R.layout.user_list);
userDialog.setTitle("请选择");
ListView lv=(ListView)userDialog.findViewById(R.id.userList);
lv.setAdapter(new MyAdapter());            
userDialog.show();

  如上,实例化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方法调用之后。

2、inflate方法与 findViewById 方法区别

inflate方法与 findViewById 方法区别 | LayoutInflater的inflate函数用法详解 :http://www.cnblogs.com/loyea/archive/2013/04/27/3047248.html
inflate()和findViewById()函数的用法:http://blog.csdn.net/tanjunjie621/article/details/7334503

  • 22
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
在 Android Fragment 中,`getActivity()` 方法返回 null 的情况通常发生在以下几种情况下: 1. 在 `onAttach()` 方法之前调用 `getActivity()`:`onAttach()` 是 Fragment 生命周期中的第一个调用方法,此时 Fragment 已经与 Activity 关联,但是 `getActivity()` 方法还没有返回有效的 Activity 对象。如果你在 `onAttach()` 之前尝试调用 `getActivity()`,它会返回 null。 2. 在 Fragment 的生命周期方法外部调用 `getActivity()`:Fragment 生命周期中的一些方法(如 `onCreateView()`、`onActivityCreated()`、`onStart()`、`onResume()` 等)之外的地方调用 `getActivity()` 方法,可能导致返回 null。这是因为这些方法在 Fragment 生命周期的不同阶段会被多次调用,只有在特定的生命周期阶段才能获取到有效的 Activity 对象。 3. 在 Fragment 被销毁后调用 `getActivity()`:如果在 Fragment 被销毁后(如 `onDestroy()` 方法被调用后)继续尝试调用 `getActivity()`,它会返回 null。这是因为 Fragment 已经与 Activity 解除关联,不能再获取到 Activity 对象。 为了避免 `getActivity()` 返回 null,你可以尝试以下几种方法: 1. 在合适的生命周期方法内调用 `getActivity()`:确保在合适的生命周期方法(如 `onCreateView()`、`onActivityCreated()`、`onStart()`、`onResume()` 等)内调用 `getActivity()`,以确保获取到有效的 Activity 对象。 2. 使用 `onAttach()` 方法中的 `context` 参数:在 `onAttach()` 方法中,可以通过 `context` 参数来获取有效的 Activity 对象。你可以将其保存为成员变量,以便在其他地方使用。 3. 使用 `getView().getContext()`:如果你在 Fragment 的视图已经创建之后需要获取有效的 Activity 对象,可以使用 `getView().getContext()` 方法来获取。 请根据你的具体情况选择适合的方法来获取有效的 Activity 对象,避免 `getActivity()` 返回 null

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值