'Android: Handling a switch in drawer menu
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_darkthe 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.)
Solution 1:[1]
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
onNavigationItemSelectedlistener, 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
onCheckedChangelistener 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();
}
}
Solution 2:[2]
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 .
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Nekomajin42 |
| Solution 2 | rahim virani |
