Remove all items in the BottomNavigationView without dummy or reflection

I am trying to use the BottomNavigationView from a design library. Everything works, except that each navigation element starts working, and so I want to remove all elements on nav so that they look the same. I tried several solutions, most of which do not work, and the last one works, but feels very hacked.

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 last checked the element, not the first.

And finally, I tried adding a dummy element 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 hides the title below it, which is important for the context in my case.

Then, I found this answer: https://stackoverflow.com/a/165454/ ... and edited my solution above to include it. In particular, I added a proguard rule, and I used this exact helper class and named the method. It looks right, it seems to work. But it’s very bad for me, because:

  • I use a dummy menu item so that no visible items are visible.
  • It adds quite a bit of code for what should be a small visual fix.
  • I read before this reflection should be avoided, if at all possible.

Is there any other, preferred, simpler way to achieve this, or is this the best we have with the current version of the library?

(As a remark, I wonder if the proguard rule is needed in this solution and what it does? I don't know anything about proguard, but this project is inherited from someone who included it).

+5
source share
4 answers

After a lot 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 } 
+3
source

If I understood your question correctly (which is possible, I don’t know), the best solution would be to address this problem. These are my assumptions about your question:

  • You have a set of actions
  • Each activity has its own BottomNavigationView method.
  • When you press BNV on one action, the selected item is selected
  • You want to cancel the selected item, because when a new activity starts, nothing is selected

If my assumptions are true, there are two best solutions:

  • Use snippets without action ( Recommended )
    • They BNV remain on one action, a fragment within changes in activity
  • Do not deselect clicks
    • Each action at startup selects the correct tile to match

However, if you want to do it your own way, I think the code below will achieve this by simply changing the affected element when it changes. (You should avoid Reflection whenever possible, this usually indicates a different architectural problem with your design)

 bnv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { item.getActionView().setSelected(false); return false; } }); 
0
source

I know that the question was asked not to use reflection, but I did not find another way to get the desired effect without using it. There is a code fix to disable the switch mode in the git repository, but who knows when this will be released. So for now (26.0.1), this code works for me. Also, the reason people say it doesn't use reflection because it is slow on Android (especially on older devices). However, for this single call, this will not affect performance. You should avoid this when parsing / serializing a lot of data, though.

The reason you need to apply the proguard rule is because proguard is messing up your code. This means that he can change the names of methods, truncate names, break classes and everything that he considers necessary so that someone can not read your source code. This rule prevents the field variable name from changing, so when you call it through 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"); } } 
0
source

@Joe Van der Vee solutions work for me. I made extension methods from it. But I believe that there are no flaws, such as suppressing @RestirctedApi!

 @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 } } } 
0
source

Source: https://habr.com/ru/post/1265450/


All Articles