NullPointerException 访问 onCreate() 中的视图

NullPointerException accessing views in onCreate()

提问人:laalto 提问时间:5/14/2014 最后编辑:chrylis -cautiouslyoptimistic-laalto 更新时间:9/7/2018 访问量:23083

问:

这是 StackOverflow 上经常发布的问题的规范问题。

我正在学习教程。我使用向导创建了一个新活动。当我尝试在我的活动中获得的方法上调用方法时,我得到了。NullPointerExceptionViewfindViewById()onCreate()

活动:onCreate()

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

布局 XML ():fragment_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="packagename.MainActivity$PlaceholderFragment" >

    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/something" />

</RelativeLayout>
java android-fragments nullpointerexception

评论


答:

74赞 laalto 5/14/2014 #1

本教程可能已过时,它试图创建基于活动的 UI,而不是向导生成的代码首选的基于片段的 UI。

视图位于片段布局 () 中,而不是活动布局 () 中。 在生命周期中太早,无法在活动视图层次结构中找到它,并返回 a。调用 上的方法会导致 NPE。fragment_main.xmlactivity_main.xmlonCreate()nullnull

首选的解决方案是将代码移动到片段中,调用膨胀的片段布局:onCreateView()findViewById()rootView

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
  View rootView = inflater.inflate(R.layout.fragment_main, container,
      false);

  View something = rootView.findViewById(R.id.something); // not activity findViewById()
  something.setOnClickListener(new View.OnClickListener() { ... });

  return rootView;
}

顺便说一句,片段布局最终将成为活动视图层次结构的一部分,并且只能通过活动发现,但只有在运行片段事务之后才能发现。待处理的片段事务在 之后执行。findViewById()super.onStart()onCreate()

评论

0赞 Zar E Ahmer 8/9/2016
findViewById 部分应位于 onActivityCreated 中。
0赞 laalto 8/9/2016
@Nepster 它可以,但不一定。
0赞 Zar E Ahmer 8/9/2016
有时,活动尚未附加到 Fragment。
1赞 laalto 8/9/2016
@Nepster 这就是为什么呼吁而不是活动。findViewById()rootView
0赞 Rohit Goyal 6/25/2014 #2

由于您已经在 中声明了 View,因此将该代码段移动到片段方法中获取 NPE 的位置。 这应该可以解决问题。fragment_main.xmlonCreateView()

2赞 EpicPandaForce 7/2/2014 #3

同意,这是一个典型的错误,因为人们在开始从事 Android 开发时通常并不真正了解 Fragments 的工作原理。为了减轻混淆,我创建了一个简单的示例代码,我最初发布在 应用程序在android模拟器中停止 ,但我也在这里发布。

示例如下:

public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
    @Override
    public void onCreate(Bundle saveInstanceState)
    {
        super.onCreate(saveInstanceState);
        this.setContentView(R.layout.activity_container);
        if (saveInstanceState == null)
        {               
             getSupportFragmentManager().beginTransaction()
                .add(R.id.activity_container_container, new ExampleFragment())
                .addToBackStack(null)
             .commit();
        }
        getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                int backCount = getSupportFragmentManager().getBackStackEntryCount();
                if (backCount == 0)
                {
                    finish();
                }
            }
        });
    }

    @Override
    public void exampleFragmentCallback()
    {
        Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
    }
}

activity_container.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/activity_container_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

ExampleFragment:

public class ExampleFragment extends Fragment implements View.OnClickListener
{
    public static interface Callback
    {
        void exampleFragmentCallback();
    }

    private Button btnOne;
    private Button btnTwo;
    private Button btnThree;

    private Callback callback;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            this.callback = (Callback) activity;
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
            throw e;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.fragment_example, container, false);

        btnOne = (Button) rootView.findViewById(R.id.example_button_one);
        btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
        btnThree = (Button) rootView.findViewById(R.id.example_button_three);

        btnOne.setOnClickListener(this);
        btnTwo.setOnClickListener(this);
        btnThree.setOnClickListener(this);
        return rootView;
    }

    @Override
    public void onClick(View v)
    {
        if (btnOne == v)
        {
            Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
        }
        else if (btnTwo == v)
        {
            Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
        }
        else if (btnThree == v)
        {
            callback.exampleFragmentCallback();
        }
    }
}

fragment_example.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <Button
            android:id="@+id/example_button_one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:text="@string/hello" 
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"/>

        <Button
            android:id="@+id/example_button_two"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_one"
            android:layout_alignRight="@+id/example_button_one"
            android:layout_below="@+id/example_button_one"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

        <Button
            android:id="@+id/example_button_three"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_two"
            android:layout_alignRight="@+id/example_button_two"
            android:layout_below="@+id/example_button_two"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

</RelativeLayout>

这应该是一个有效的示例,它显示了如何使用 Activity 来显示 Fragment,并处理该 Fragment 中的事件。以及如何与包含的 Activity 进行通信。

评论

1赞 EpicPandaForce 7/2/2014
此示例将 android-support-v4 库用于 FragmentActivity 和支持 Fragment 管理器。
1赞 EpicPandaForce 7/31/2014
一个更完整的例子可以在 stackoverflow.com/questions/24840509/...
1赞 EpicPandaForce 3/15/2015
尽管您应该用于通信而不是回调,并用于注入视图。OttoButterknife
11赞 Tarun Sharma 8/29/2014 #4

尝试 OnStart() 方法并仅使用

View view = getView().findViewById(R.id.something);

或在 onStart() 中使用方法声明任何视图getView().findViewById

声明点击侦听器查看方式anyView.setOnClickListener(this);

评论

0赞 Daniel Wilson 3/16/2015
对我来说,这很有用,因为我试图访问片段的 onCreateView 中的同级视图,其中片段是在 xml 中声明的。同级视图在 onCreateView 中仍为 null,因为父视图尚未完成膨胀,但在 onStart 中,它们已:)
1赞 Joyce 9/9/2014 #5

activity_main.xml中添加以下内容

<fragment
    android:id="@+id/myFragment"
    android:name="packagename.MainActivity$PlaceholderFragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
</fragment>
0赞 Akshay Paliwal 2/27/2015 #6

在上面发布的问题代码中有一个问题: 您在 onCreate 方法中使用R.layout.activity_main,但 XML 文件名为“fragment_main.xml”,这意味着您正在尝试获取未显示fragment_main.xml文件的视图,因此它给出空指针异常。更改代码,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_main);// your xml layout ,where the views are

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}
2赞 Krishna Satwaji Khandagale 12/23/2015 #7

您正在尝试访问 but 中的 UI 元素,现在访问它们还为时过早,因为在 fragment 中可以在 method 中创建视图。 并且 方法对于处理它们的任何操作是可靠的,因为活动在这种状态下是完全加载的。onCreate()onCreateView()onActivityCreated()

0赞 clebertx 2/26/2016 #8

我在调用和方法后初始化侦听器是一样的。NullPointerExceptionfindViewById()onCreate()onCreateView()

但是当我使用它时,它起作用了。因此,我可以访问并设置我的侦听器。onActivityCreated(Bundle savedInstanceState) {...}GroupView

我希望它对你有所帮助。

3赞 Sarthak Sharma 4/6/2017 #9

尝试将访问视图转移到 fragment 的 onViewCreated 方法,因为有时当您尝试访问 onCreate 方法中的视图时,它们不会在此时呈现,从而导致空指针异常。

 @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
     View something = findViewById(R.id.something);
     something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

     if (savedInstanceState == null) {
           getSupportFragmentManager().beginTransaction()
            .add(R.id.container, new PlaceholderFragment()).commit();
    }
 }
2赞 Shubham Goel 6/6/2017 #10

视图“某物”位于 fragment 中,而不是在 activity 中,因此,您必须在 fragment 类中访问它,而不是在 activity 中访问它,例如

在占位符片段.class中

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
  false);

View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });

return root;
}
0赞 Mohan 6/27/2017 #11

你必须记住重要的事情是: 当您声明了变量并尝试在为其赋值之前检索其值时,会发生 NullPointerException。

0赞 Sandip Savaliya 12/13/2017 #12

每当使用或调用片段中的视图时,请使用 onViewCreated() 方法。

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
      View v = view.findViewById(R.id.whatever)
}
0赞 Rishabh Saxena 6/4/2018 #13

用于查找视图的最流行的库,几乎每个开发人员都使用。

黄油刀

据我所知,有足够的答案来解释用适当的方法寻找观点。但是,如果您是 android 开发人员并且每天经常编写代码,那么您可以使用黄油刀,它可以节省大量查找视图的时间,并且您无需为其编写代码,只需 2-3 个步骤,您就可以在几毫秒内找到视图。

在应用级 gradle 中添加依赖项:

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

添加黄油刀插件:

File -> Settings -> plugins-> 

然后搜索 Android ButterKnife Zelezny 并安装插件并重新启动您的工作室,您就完成了它。

现在只需转到活动的 Oncreate 方法,右键单击您的layout_name并点击生成按钮并选择黄油刀注射选项,您的视图引用将自动创建,如下所示:

    @BindView(R.id.rv_featured_artist)
    ViewPager rvFeaturedArtist;
    @BindView(R.id.indicator)
    PageIndicator indicator;
    @BindView(R.id.rv_artist)
    RecyclerView rvArtist;
    @BindView(R.id.nsv)
    NestedScrollingView nsv;
    @BindView(R.id.btn_filter)
    Button btnFilter;