BottomBar
BottomBar copied to clipboard
Select Tab without triggering Listener
Is there a way to change the BottomBar programmatically without triggering the listener? The app has a navigation path using a deeplink, the navigation is handled correctly (A bundle is passed for Fragment creation). When we change the selected tab (so the correct item is highlighted), the listener is called and causes the fragment to reload a second time.
I tried to work around it by setting the listener to null, selected the correct tab, then re attaching the listener; however when the listener is attached it is called on the currently selected tab.
Any ideas?
Thanks!
//Mark.
It is also a problem when using Conductor or similar libraries which restore the views with the savedInstanceState bundle. The listener should probably have a "fromUser" boolean parameter to avoid this issue.
I guess you could solve your problem by keeping a local boolean "tabListenerActive" which you can toggle when you want to disable the listener.
Setting listener to null won't help. I solved (or better say workaround-ed) the problem by using this inherited version of bottombar (it's essentially the solution chip2n proposed above). In my OnTabSelectListener I'm doing mBottomBar.isManaged() check. `public class ManagedBottomBar extends BottomBar { @Getter private boolean managed;
public ManagedBottomBar(Context context) {
super(context);
}
public ManagedBottomBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void selectTabAtPosition(int position, boolean managed) {
this.managed = managed;
super.selectTabAtPosition(position);
}
@Override
public void selectTabAtPosition(int position) {
selectTabAtPosition(position, false);
}
}`
I had to go another route. I pulled down the raw code and made a hack. I didn't want to change too much code and interrupt anything else as a side effect.
Here is what I did:
I added a boolean (a getter and setter) to BottomBar.java:
private boolean triggerListeners = true;
Then I adjusted the updateSelectedTab method:
private void updateSelectedTab(int newPosition) {
int newTabId = getTabAtPosition(newPosition).getId();
if (newPosition != currentTabPosition) {
if ((onTabSelectListener != null) && triggerListeners) {
onTabSelectListener.onTabSelected(newTabId);
}
} else if (onTabReselectListener != null && !ignoreTabReselectionListener && triggerListeners) {
onTabReselectListener.onTabReSelected(newTabId);
}
currentTabPosition = newPosition;
if (ignoreTabReselectionListener) {
ignoreTabReselectionListener = false;
}
}
Then, when I change the tab via my application code; I turn off the listener, make the change, then turn the listener back on.
mNavigationBar.setTriggerListeners(false);
mNavigationBar.selectTabWithId(tabId);
mNavigationBar.setTriggerListeners(true);
This allowed all of the normal changing of the tab (colors, animation, etc) to occur, but did not call the listener. It allowed me to manage the changing of the Fragment.
There are a 100 different ways to achieve the same thing (change the method signature, different code paths, etc), but I wanted to have as little impact to the existing code as possible.
The following worked for me using latest version 2.2.0
public void highlightBottomBarMenu(int position) {
bottomBar.setOnTabSelectListener(null);
selectBottomBarMenu(position, false);
bottomBar.setOnTabSelectListener(this, false);
}
i still wish there was a simple api available for this.
Note on above: reselect triggers listener too, so you may want to:
private void updateBottomBarSelection(int index){
bottomBar.setOnTabSelectListener(null);
bottomBar.setOnTabReselectListener(null);
bottomBar.selectTabAtPosition(index, true);
bottomBar.setOnTabSelectListener(this, false);
bottomBar.setOnTabReselectListener(this);
}
Now possible with TabSelectionInterceptor.
@roughike TabSelectionInterceptor does not appear to solve this problem; it solves a different use case. Setting TabSelectionInterceptor suppresses changes on click, while we want to update the UI with no tapping/clicking.
My bad, I misunderstood.
Yea a feature where you can trigger UI change without triggering selection would be great to have. That would fix some of the issues related to fragment backstack and deeplinking.
@binaryKarmic's code does not work in 2.3.1. It throws following error:
java.lang.NullPointerException: Attempt to invoke interface method 'void com.roughike.bottombar.OnTabSelectListener.onTabSelected(int)' on a null object reference
You can fix it by removing OnTabSelectListener, then selecting tab and then applying listener again.
public void highlightBottomBarMenu(int position) {
bottomBar.removeOnTabSelectListener();
bottomBar.selectTabAtPosition(position, false);
bottomBar.setOnTabSelectListener(this, false);
}
We can do so by changing state for desired item (Kotlin):
val menu: Menu = mBinding.navBar.menu
val item: MenuItem = menu.findItem(R.id.my_id)
item.isChecked = true
public void highlightBottomBarMenu(int position) { bottomBar.removeOnTabSelectListener(); bottomBar.selectTabAtPosition(position, false); bottomBar.setOnTabSelectListener(this, false); }
Where do you implement this?