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();
}
}