尝试查找菜单项时应用程序在屏幕旋转时崩溃

App crashes on screen rotation when trying to find menu item

我在我的应用程序中使用了 collapsingToolbarLayout,只要菜单没有折叠,旋转 phone 就没有问题。但是当菜单折叠并且只有原始工具栏显示应用程序崩溃时。问题是,当只有原始工具栏显示时,可点击项目出现并在显示折叠工具栏时消失。旋转 phone 时,应用程序找不到此项。我该如何解决?

菜单所在的 activity。

private Menu menu;
protected void onCreate(Bundle savedInstanceState) { 
    ...
    AppBarLayout mAppBarLayout = findViewById(R.id.appBarLayout2);
    mAppBarLayout.addOnOffsetChangedListener(new 
    AppBarLayout.OnOffsetChangedListener() {

            int scrollRange = -1;

            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int 
            verticalOffset) {
                if (scrollRange == -1) {
                    scrollRange = appBarLayout.getTotalScrollRange();
                }
                if (scrollRange + verticalOffset == 0) {
                    isShow = true;
                    showOption();
                } else if (isShow) {
                    isShow = false;
                    hideOption();
                }
            }
        });

    }

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        this.menu = menu;
        getMenuInflater().inflate(R.menu.menu_scrolling, menu);
        hideOption();
        return true;
    }

private void hideOption() {
        MenuItem item = menu.findItem(R.id.action_info);
        item.setVisible(false);
    }

    private void showOption() {
        MenuItem item = menu.findItem(R.id.action_info);
        item.setVisible(true);
    }

xml-文件中的相关代码:

<android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout2"
        android:layout_width="match_parent"
        android:layout_height="128dp"
        android:theme="@style/AppTheme.Base"
        app:layout_constraintTop_toTopOf="@+id/nestedScrollView">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsTool"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorSecondary"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="bottom|center"
            app:expandedTitleMargin="16dp"
            app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Display2"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="@string/title_expenses">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:navigationIcon="@drawable/ic_action_exit"
                app:title="@string/title_expenses"
                app:titleTextColor="@android:color/background_light" />

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

menu_Scrolling.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.journaldev.collapsingtoolbarlayout.ScrollingActivity">

    <item
        android:id="@+id/action_info"
        android:icon="@drawable/ic_action_add"
        android:orderInCategory="200"
        android:title="Add"
        app:showAsAction="ifRoom" />

</menu>

这是错误信息:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.korneliapalm.android.samboappen/com.korneliapalm.android.samboappen.MoneyListActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'android.view.MenuItem android.view.Menu.findItem(int)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257)
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5039)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4948)
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:69)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7050)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'android.view.MenuItem android.view.Menu.findItem(int)' on a null object reference
        at com.korneliapalm.android.samboappen.MoneyListActivity.showOption(MoneyListActivity.java:163)
        at com.korneliapalm.android.samboappen.MoneyListActivity.onCreate(MoneyListActivity.java:51)
        at android.app.Activity.performCreate(Activity.java:7327)
        at android.app.Activity.performCreate(Activity.java:7318)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3094)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5039) 
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4948) 
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:69) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7050) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 

Activity:

当发生方向改变时,android 重新启动 运行 Activity(调用 onDestroy(),然后调用 onCreate())。如果你想在 activity 内部处理,你不能使用:在 manifest 文件中 particular activity.

API12 岁及以下:

android:configChanges="orientation"

如果您的目标是 API 13 岁或以上

android:configChanges="orientation|screenSize"

问题在于,在 onCreate 中,您正在调用方法 hideOption()showOption()。 但是,oncreate 在 onCreateOptionsMenu 之前被调用,这是您展开菜单视图的地方。所以调用 menu.findItem(R.id.action_info); 会触发一个空指针。

getMenuInflater().inflate(R.menu.menu_scrolling, menu);

在onCreate中调用这两个方法之前,需要确保菜单视图是膨胀的。

private void hideOption() {
    if (menu == null) return;
    MenuItem item = menu.findItem(R.id.action_info);
    item.setVisible(false);
}

private void showOption() {
    if (menu == null) return;
    MenuItem item = menu.findItem(R.id.action_info);
    item.setVisible(true);
}

您没有在代码中处理方向。您应该尝试处理它们。 崩溃的原因是当您旋转屏幕时发生以下事件:

  1. Activity 被摧毁。
  2. Activity 重新创建。
  3. AppBarLayout 的偏移量发生变化,方法 onOffsetChanged 被调用。
  4. if (scrollRange + verticalOffset == 0) { 条件成立并调用 showOption(); 方法。
  5. 因为正在重新创建 Activity,所以在 showOption(); 方法中使用的菜单项是 null 在行 MenuItem item = menu.findItem(R.id.action_info);
  6. 因为 item 为空,代码 item.setVisible(true); 产生 Null Pointer Exception.
  7. 的运行时崩溃

处理方向的正确方法是在 Activity 代码中实现以下方法:

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks the orientation of the screen
    if (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show()
    } else if (newConfig.orientation === Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show()
    }
}

作为预防措施,您可以像这样添加空值检查来暂时避免崩溃:

private void hideOption() {
    MenuItem item = menu.findItem(R.id.action_info);
    if (item != null) {
        item.setVisible(false);
    } else {
        Log.e("Your Class", "hideOption() was called, but menu item was null");
    }
}

private void showOption() {
    MenuItem item = menu.findItem(R.id.action_info);
    if (item != null) {
        item.setVisible(true);
    } else {
        Log.e("Your Class", "showOption() was called, but menu item was null");
    }
}

希望对您有所帮助。

参考link: https://developer.android.com/guide/topics/resources/runtime-changes