如何在onCreate中使用findViewById()而不返回null?

3

我不知道如何使用findViewById(R.id.nav_timetable);而不返回null并创建NPE。我的目标是默认加载TimetableFragment,然后人们可以使用抽屉导航去到其他片段。

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

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    setupDrawer(toolbar);

    drawer.openDrawer(GravityCompat.START); // Opened this on first run so they know what's in it.

    // TODO Change this later to a home page and remove goToTimetablePage();
    // goToTimetablePage();
    goToFragmentFromNavMenuItem((MenuItem) findViewById(R.id.nav_timetable));
}

@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
    // Handle navigation view item clicks here.

    goToFragmentFromNavMenuItem(menuItem);
    return true;
}

private void goToFragmentFromNavMenuItem(MenuItem menuItem) {
    // Create a new fragment and specify the fragment to show based on nav item clicked
    Fragment fragment = null;
    Class fragmentClass;

    switch (menuItem.getItemId()) {
        case R.id.nav_slideshow: // TODO fragmentClass = SlideshowFragment.class; break;
        case R.id.nav_share: // TODO fragmentClass = ShareFragment.class; break;
        case R.id.nav_timetable:
            fragmentClass = TimetableFragment.class;
            // goToTimetablePage();
            break;
        default:
            fragmentClass = TimetableFragment.class;
            // TODO Change this later to a home page:
            // TODO fragmentClass = HomeFragment.class; Remove "goToTimetablePage();"
    }


    //noinspection TryWithIdenticalCatches
    try {
        fragment = (Fragment) fragmentClass.newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

    FragmentManager fragmentManager = getSupportFragmentManager();

    fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit();

    menuItem.setChecked(true);
    setTitle(menuItem.getTitle());
    drawer.closeDrawer(GravityCompat.START);
}

错误信息:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.gmail.gogobebe2.thedayahead/com.gmail.gogobebe2.thedayahead.MainActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'int android.view.MenuItem.getItemId()' on a null object reference
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
  at android.app.ActivityThread.-wrap11(ActivityThread.java)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
  at android.os.Handler.dispatchMessage(Handler.java:102)
  at android.os.Looper.loop(Looper.java:148)
  at android.app.ActivityThread.main(ActivityThread.java:5417)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
  Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int android.view.MenuItem.getItemId()' on a null object reference
  at com.gmail.gogobebe2.thedayahead.MainActivity.goToNavFragment(MainActivity.java:123)
  at com.gmail.gogobebe2.thedayahead.MainActivity.onCreate(MainActivity.java:59)
  at android.app.Activity.performCreate(Activity.java:6237)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
  at android.app.ActivityThread.-wrap11(ActivityThread.java) 
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
  at android.os.Handler.dispatchMessage(Handler.java:102) 
  at android.os.Looper.loop(Looper.java:148) 
  at android.app.ActivityThread.main(ActivityThread.java:5417) 
  at java.lang.reflect.Method.invoke(Native Method) 
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

我尝试使用onCreateView方法,但某些原因导致出现了stackoverflow错误信息。

编辑:这里是所有的代码,以消除任何困惑:https://github.com/gogobebe2/TheDayAhead

编辑2:不要担心goToTimetablePage()方法。目前没有使用它,我计划将它移到TimetableFragment片段中。

编辑3:我将方法名称更改为goToFragmentFromNavMenuItem(),以更好地描述其操作。我还想告诉大家,onNavigationItemSelected(MenuItem menuItem) 方法与 goToFragmentFromNavMenuItem()方法完美配合使用,只是在onCreate(Bundle savedInstanceState)中使用时由于findViewById(R.id.nav_timetable)始终返回null而无法使用。

另外,这是我制作的第一个Android应用程序,我正在制作中学习。昨晚我甚至不知道什么是片段,所以我真的很新手,请不要假设任何知识。


请发布您的 R.layout.activity_main。 - Tomer Shemesh
3个回答

2
为了避免由NPE引起的崩溃,您可以在传递参数之前测试返回值是否为null:
MenuItem menuItem = (MenuItem) findViewById(R.id.nav_timetable);
if (menuItem != null) {
    goToNavFragment(menuItem);
}

无论如何,这并不能解决你的问题,实际上findViewById(int)返回null是因为你在Activity布局中查找的视图ID不存在。
方法findViewById()来自于View类,你正在调用它,并在this上执行它,而this是一个Activity(扩展View的类),搜索当前充气布局中的视图id(从R中)。
现在,如果你看一下你的onCreate实现,你会发现你正在调用setContentViewMethod(R.layout.activity_main);这意味着你正在设置此活动的布局,将其从具有id activity_main的xml布局中获取。
这里的大问题是你传递的布局(activity_main)不包含你要查找的id。只需查看activity_main.xml布局文件,你会发现没有带有id R.id.nav_timetable的对象。
一个Activity一次只能加载一个布局活动文件。你仍然可以为菜单或特定视图对象(SpinnerList等)加载其他布局文件。
在这里最重要的事情是你试图检索一个MenuItem;为了实现这一点,你必须覆盖两个方法onCreateOptionsMenu()onOptionsItemSelected()onCreateOptionsMenu()执行它的名字所说的。它创建你要使用的选项菜单。在这里,你可以挂接你的操作,但你必须做的最重要的事情是填充一个菜单布局。如果没有这个,你将无法应用自定义菜单布局并使用findViewById
因此,在调用findViewById()之前,你应该这样做:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.your_menu_id, menu);

    // findViewById()

    return true;
}

onOptionsItemSelected()方法将在您之前创建的菜单中选择一个项目时被调用,并将选定的MenuItem作为参数传递给您,在这里,您可以实现菜单项点击操作。一个标准的实现可能是以下内容:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_one:
            doSomething()
            return true;
        case R.id.action_two:
            doSomethingElse();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

1
抱歉,我在问题中应该提供更多信息。不过我已经编辑过了。 - gogobebe2
1
@gogobebe2 不用担心误解,看一下修改后的内容就好了 ;) - fredmaggiowski
1
太好了!非常感谢!它起作用了!!现在我需要去找出“inflate”的意思:) 我会在24小时内给你50个声望,谢谢。 - gogobebe2
1
不用谢!哈哈让我们说“充气”意味着(大约)将某物插入到另一物中,在这种情况下,我们将一个布局XML插入到Java类中,因此我们正在对其进行充气。祝你有美好的一天 :) - fredmaggiowski
1
你所说的那部分内容:“一个Activity一次只能加载一个布局文件。但你仍可以为菜单或特定视图对象(如Spinner,List等)加载其他布局文件。”绝对是一些非常重要的信息,帮助我理解了我哪里错了。谢谢 :) - gogobebe2
显示剩余9条评论

0
为了避免返回 null,需要向其传递一个有效的视图 Id。在您的 XML 设计文件中查找并像这样在函数中使用该 ID:

rootview.findViewById(R.id.your_id_here)


我使用了有效的ID。请查看编辑以获取完整代码。 - gogobebe2
请查看我对问题所做的修改,谢谢 :) - gogobebe2

0

由于您的问题没有包括布局文件,我假设R.id.nav_timetable是指片段的ID(并且它是您的布局的一部分)?如果是这样,您需要使用FragmentManager(或SupportFragmentManager)中的findFragmentById()而不是findViewById()


R.id.nav_timetable 指的是抽屉中的 MenuItem。 - gogobebe2
请查看我对问题所做的修改,谢谢 :) - gogobebe2

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