I am attempting to use the BottomNavigationView from the design library. Everything is working except I want each navigation item to start an activity, and therefore I want to uncheck all items in the nav so they look the same. I have tried several solutions, most of which do not work, and the last of which does work but feels very hacky.
First I did this:
ViewGroup nav = (ViewGroup) bottomNav;
for(int i=0; i < nav.getChildCount(); i++) {
nav.getChildAt(i).setSelected(false);
}
Which seemed to do nothing.
Then I tried:
int size = bottomNav.getMenu().size();
for (int i = 0; i < size; i++) {
bottomNav.getMenu().getItem(i).setChecked(false);
}
Which only made the last item checked instead of the first.
And finally I tried adding a dummy item to the menu and doing:
bottomNav.getMenu().findItem(R.id.dummmy_item).setChecked(true);
bottomNav.findViewById(R.id.dummmy_item).setVisibility(View.GONE);
Which almost works, but it hides the title underneath, which are important for context in my case.
Then I found this answer: https://stackoverflow.com/a/41372325/4888701 and edited my above solution to include that. Specifically I added the proguard rule, and I used that exact helper class and called the method. It looks correct, seems to work. But it feels very hacky to me because:
Is there any other, preferably simpler way to achieve this, or is this the best we have with the current version of the library?
(As a side note, I am wondering if the proguard rule in this solution is necessary and what it does? I don't know really anything about proguard, but this project is inherited from someone else who had enabled it.)
After plenty of trial and error, this worked for me (using Kotlin)
(menu.getItem(i) as? MenuItemImpl)?.let {
it.isExclusiveCheckable = false
it.isChecked = it.itemId == actionId
it.isExclusiveCheckable = true
}
The @Joe Van der Vee solutions works for me. I have made extension methods from it. But I consider whether this doesn't have some downsides like @RestirctedApi suppressing!
@SuppressLint("RestrictedApi")
fun BottomNavigationView.deselectAllItems() {
val menu = this.menu
for(i in 0 until menu.size()) {
(menu.getItem(i) as? MenuItemImpl)?.let {
it.isExclusiveCheckable = false
it.isChecked = false
it.isExclusiveCheckable = true
}
}
}
If I've understood your question correctly (which it's possible I haven't) then a better solution might be to flip this problem around. These are my assumptions about your question:
If my assumptions are correct there are two better solutions:
That said, if you do want to do it your way I think the code below will achieve it, by just changing the affected item when it changes. (You should avoid Reflection whenever possible, it's generally indicative of another architectural problem with your design)
bnv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
item.getActionView().setSelected(false);
return false;
}
});
I know the question asked to not use reflection, however, I have not found another way to get the desired effect without using it. There is a code fix for allowing to disable the shifting mode in the git repo but who knows when that will be released. So for the time being (26.0.1), this code works for me. Also the reason people say don't use reflection is because it is slow on Android (especially on older devices). However, for this one call it won't be an impact on performance. You should avoid it when parsing/serializing a large amount of data though.
The reason for needing the proguard rule is because proguard obfuscates your code. Which means it can change method names, truncate names, break up classes and whatever else it sees fit to prevent someone from being able to read your source code. This rule prevents this field variable name from changing so that when you call it via reflection, it still exists.
Proguard rule:
-keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
boolean mShiftingMode;
}
Updated method:
static void removeShiftMode(BottomNavigationView view)
{
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try
{
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++)
{
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
item.setShiftingMode(false);
item.setChecked(false); // <-- Changed this line
item.setCheckable(false); // <-- Added this line
}
}
catch (NoSuchFieldException e)
{
Log.e("ERROR NO SUCH FIELD", "Unable to get shift mode field");
}
catch (IllegalAccessException e)
{
Log.e("ERROR ILLEGAL ALG", "Unable to change value of shift mode");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With