0

I have only one user setting in my app, and I want to put it into the navigation drawer with a switch added to the given menu item.

Here is the relevant menu code:

<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" />

The switch does appear on the right side of the menu item.

My listener:

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

My problems:

  • When I tap on R.id.nav_dark the item gets selected. I would like the colored overlay to stay on (or jump back to) the previous menu item, whose fragment is actually shown behind the drawer.
  • The switch does not react accordingly, even if I use item.setChecked(true) manually. I would like the switch to be turned on when the dark theme is enabled and turned off when it's disabled.
  • Tapping on the switch itself does not pass the event to the menu item. I would like them to work in sync.

I have seen checkboxes and swiches working like this in other applications, although, most of them were in the app bar's overflow menu. (I have tried mine with a checkbox too, but no difference.)

Nekomajin42
  • 638
  • 1
  • 6
  • 20

2 Answers2

2

I solved this problem using this thread: Switch in Navigation drawer item with Design Support Library on Android

In this example the dedicated menu item switches between a light and dark theme, but you can use it to toggle any settings, of course.

Problems to solve

  • Implement our own onNavigationItemSelected listener, because the default solution created by Android Studio prevents the use of a dedicated menu item.
  • Implement the fragment transaction and toolbar handling logic.
  • Implement the onCheckedChange listener of the switch.

What we do is capture clicks on the menu items. If it's a regular item, we change the fragment behind the drawer. If it's the dedicated item with the switch, we manually toggle the switch, which calls its listener.

The actual code (changing the theme in this case) is handled by the listener of the switch. If you click on the switch itself, the listener will be called directly.

Relevant code from activity_main_drawer.xml menu file

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

Relevant code from 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();
    }
}
Nekomajin42
  • 638
  • 1
  • 6
  • 20
0

try this one.

  <item
        app:actionViewClass="androidx.appcompat.widget.SwitchCompat"
        android:icon="@drawable/message"
        android:title="All inboxes"
        android:id="@+id/inbox"
        />

this above is the menu item we need

in order to get click events follow below steps mentioned

        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            if (item.getItemId()==R.id.inbox){
                ((SwitchCompat) item.getActionView()).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        if (isChecked){
                            Toast.makeText(buttonView.getContext(), "Checked", Toast.LENGTH_SHORT).show();
                        }
                        else {
                            Toast.makeText(buttonView.getContext(), "unChecked", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        Toast.makeText(MainActivity.this, ""+item.getTitle(), Toast.LENGTH_SHORT).show();
            drawerLayout.closeDrawer(GravityCompat.START);
            return true;
        }
    });

we already given our action class in menu item from xml . we just need to verify which item it was and when it happens we can get the actionviewclass that we had assigned and an onclicklistener on it .. this one worked for me .