BottomBar icon indicating copy to clipboard operation
BottomBar copied to clipboard

Select Tab without triggering Listener

Open mtheadley opened this issue 9 years ago • 12 comments

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.

mtheadley avatar Sep 16 '16 19:09 mtheadley

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.

chip2n avatar Sep 17 '16 15:09 chip2n

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

}`

oradkovsky avatar Sep 20 '16 19:09 oradkovsky

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.

mtheadley avatar Sep 26 '16 14:09 mtheadley

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.

binaryKarmic avatar Feb 03 '17 12:02 binaryKarmic

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

narfman0 avatar Feb 24 '17 16:02 narfman0

Now possible with TabSelectionInterceptor.

roughike avatar Apr 06 '17 18:04 roughike

@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.

narfman0 avatar Apr 06 '17 18:04 narfman0

My bad, I misunderstood.

roughike avatar Apr 06 '17 19:04 roughike

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.

TostF avatar May 16 '17 14:05 TostF

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

drobilc avatar Jun 11 '17 13:06 drobilc

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

lena-abarova avatar Mar 29 '21 09:03 lena-abarova

public void highlightBottomBarMenu(int position) {
    bottomBar.removeOnTabSelectListener();
    bottomBar.selectTabAtPosition(position, false);
    bottomBar.setOnTabSelectListener(this, false);
}

Where do you implement this?

snetts avatar Apr 20 '22 11:04 snetts