Android:处理抽屉菜单中的开关

问题描述

我的应用程序中只有一个用户设置,我想通过添加到给定菜单项的开关将其放入导航抽屉。

这是相关的菜单代码

<item
    android:id="@+id/nav_dark"
    android:checkable="true"
    android:icon="@drawable/round_brightness_4_24"
    android:title="@string/menu_dark"
    app:actionViewClass="android.widget.Switch" />

该开关确实出现在菜单项的右侧。

我的听众:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {

    if (item.getItemId() == R.id.nav_dark) {
        // code to apply dark or light theme
        // works as expected
    }
    else {
        // code to handle regular menu items
        // it works too
    }

    return true;
}

我的问题:

  • 当我点击R.id.nav_dark时,该项目被选中。我希望彩色的覆盖物停留在(或跳回)上一个菜单项,该菜单项的片段实际上显示在抽屉后面。
  • 即使我手动使用item.setChecked(true),开关也不会做出相应的反应。我希望启用深色主题时将开关打开,禁用深色主题时将开关关闭
  • 点击开关本身不会将事件传递给菜单项。我希望他们同步工作。

我在其他应用程序中看到了复选框和电子表格的工作方式,尽管其中大多数都在应用程序栏的溢出菜单中。 (我也尝试过使用复选框,但没有区别。)

解决方法

我使用以下线程解决了这个问题:Switch in Navigation drawer item with Design Support Library on Android

在此示例中,专用菜单项在浅色和深色主题之间切换,但是您可以使用它来切换任何设置。

问题解决

  • 实施我们自己的onNavigationItemSelected侦听器,因为Android Studio创建的默认解决方案阻止使用专用菜单项。
  • 实施片段交易和工具栏处理逻辑。
  • 实施交换机的onCheckedChange侦听器。

我们要做的是捕获菜单项上的点击。如果是常规物品,我们将更改抽屉后面的片段。如果它是开关的专用项,我们将手动切换该开关,从而调用其监听器。

实际的代码(在这种情况下,更改主题)由开关的侦听器处理。如果单击开关本身,则将直接调用侦听器。

activity_main_drawer.xml菜单文件中的相关代码

<item
    android:id="@+id/nav_dark"
    android:checkable="true"
    android:icon="@drawable/round_brightness_4_24"
    android:title="@string/menu_dark"
    app:actionViewClass="android.widget.Switch" /> <!-- you can also use a CheckBox -->

MainActivity.java中的相关代码

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,Switch.OnCheckedChangeListener {

    private Toolbar toolbar;
    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle toggle;
    private NavigationView navigationView;
    private Switch switchDark;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle(getResources().getString(R.string.toolbar_title));
        setSupportActionBar(toolbar);

        // We have to handle the fragment changes manually,// because what we do conflicts with the default solution created by Android Studio
        drawerLayout = findViewById(R.id.drawer_layout);
        toggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.navigation_drawer_open,R.string.navigation_drawer_close);
        drawerLayout.addDrawerListener(toggle);
        toggle.setDrawerIndicatorEnabled(true);
        toggle.syncState();

        navigationView = findViewById(R.id.nav_view);
        // Check the menu item connected to the default fragment manually
        navigationView.getMenu().findItem(R.id.nav_item1).setChecked(true);
        navigationView.setNavigationItemSelectedListener(this); // See below!

        switchDark = (Switch)navigationView.getMenu().findItem(R.id.nav_dark).getActionView();
        // Set the default state of the switch connected to the menu item
        switchDark.setChecked(AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES);
        switchDark.setOnCheckedChangeListener(this); // See below!
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        // Handle the menu item with the switch
        if (item.getItemId() == R.id.nav_dark) {
            ((Switch)item.getActionView()).toggle(); // Call the onCheckedChangeListener of the switch and let it do the work
            return false; // Prevent the menu item to get selected (No overlay indicator will appear)
        }

        // Handle the other menu items
        // We have to do this,because we deleted the default solution created by Android Studio
        Fragment newFragment = null;

        if (item.getItemId() == R.id.nav_item1) {
            newFragment = new CustomFragment();
            toolbar.setTitle(getResources().getString(R.string.custom_fragment_title));
        }
        else if (item.getItemId() == R.id.nav_item2) {
            newFragment = new OtherFragment();
            toolbar.setTitle(getResources().getString(R.string.other_fragment_title));
        }

        // Start the fragment transition manually
        if (newFragment != null) {
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.nav_host_fragment,newFragment);
            transaction.addToBackStack(null);
            transaction.commit();
            drawerLayout.close();
        }

        return true; // The selected item will have the overlay indicator
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(buttonView.getContext());
        SharedPreferences.Editor editor = sharedPreferences.edit();

        int themeID;
        if (isChecked) {
            themeID = AppCompatDelegate.MODE_NIGHT_YES;
        }
        else {
            themeID = AppCompatDelegate.MODE_NIGHT_NO;
        }

        AppCompatDelegate.setDefaultNightMode(themeID); // Change the theme at runtime
        editor.putInt("themeID",themeID); // Save it to be remembered at next launch
        editor.apply();
    }
}