在onCreate()中访问视图时出现了NullPointerException异常。

121

这是一个经常出现在StackOverflow上的问题的规范问题。

我正在跟随教程。我使用向导创建了一个新的活动。在我的活动onCreate()中,当尝试调用使用findViewById()获得的View上的方法时,我遇到了NullPointerException

活动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>
13个回答

74

这篇教程可能已过时,试图创建基于活动的用户界面而不是向导生成代码所偏爱的基于片段的用户界面。

视图位于片段布局 (fragment_main.xml) 中而不是活动布局 (activity_main.xml) 中。在生命周期的onCreate()阶段查找它在活动视图层次结构中太早了,会返回空值null。在null上调用方法会导致NPE。

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

@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()发现,但只有在片段事务运行后才能这样做。待定的片段事务会在onCreate()之后的super.onStart()中执行。


findViewById 部分应该在 onActivityCreated 中。 - Zar E Ahmer
@Nepster 可以是,但不一定要这样。 - laalto
有时候活动尚未附加到片段。 - Zar E Ahmer
1
@Nepster 这就是为什么要在rootView上调用findViewById()而不是在活动上调用的原因。 - laalto

11

尝试使用 OnStart() 方法,只需使用

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

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

通过 anyView.setOnClickListener(this); 为视图声明点击监听器。


对我来说这很有用,因为我正在尝试在片段的onCreateView中访问一个兄弟视图,其中该片段在xml中声明。在onCreateView中,兄弟视图仍为空,因为父级还没有完成膨胀,但是在onStart中它们已经存在 :) - Daniel Wilson

3
尝试将访问视图的方法转移到片段的onViewCreated方法中,因为有时在onCreate方法中尝试访问视图时,它们可能还没有被渲染,导致空指针异常。请注意保留HTML标记。
 @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

视图“something”位于片段中而不是活动中,因此您必须在片段类中访问它,而不是在活动中访问它,例如:

在 PlaceholderFragment.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;
}

2

同意,这是一个典型的错误,因为很多人在开始进行安卓开发时并不真正了解Fragment的工作原理。为了减轻困惑,我创建了一个简单的示例代码,最初发布在Application is stopped in android emulator,但我也在这里发布了它。

以下是一个示例:

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>

示例片段:

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
此示例使用android-support-v4库来支持FragmentActivity和支持片段管理器。 - EpicPandaForce
1
一个更完整的例子可以在http://stackoverflow.com/questions/24840509/why-does-the-new-adt-create-a-static-inner-class-fragment-by-default-simple-fr找到。 - EpicPandaForce
1
虽然你应该使用Otto来进行通信而不是回调函数,并且使用Butterknife来注入视图。 - EpicPandaForce

2

您正在尝试在onCreate()中访问UI元素,但是此时访问它们还为时过早,因为在片段视图中可以在onCreateView()方法中创建。而onActivityCreated()方法可靠地处理对它们的任何操作,因为在此状态下活动已完全加载。


1
请在你的 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
在编写Fragment时,如果需要使用或调用视图,请使用onViewCreated()方法。
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
      View v = view.findViewById(R.id.whatever)
}

0

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

ButterKnife

我可以看出有足够的答案解释适当的方法来查找视图。但是如果您是Android开发人员并且每天经常编写代码,则可以使用Butter-Knife,在查找视图时节省了很多时间,并且您不必为此编写代码,只需2-3步即可在毫秒级别查找视图。

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

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

为Butter Knife添加插件:

File -> Settings -> plugins-> 

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

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

    @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;

0

由于您已经在fragment_main.xml中声明了您的视图,请将代码片段移动到片段的onCreateView()方法中,以解决NPE问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接