Bottombar: fragments re-create each time I select bottomBar items

Created on 29 Jul 2016  路  20Comments  路  Source: roughike/BottomBar

how can I avoid that ?

Most helpful comment

If anyone is wondering, here's my solution based on the idea of @jcancellier .

private static final String ARG_PARAM1 = "param1";

FragmentMainHolderBinding mBinder;
FragmentManager mFragmentManager;
Fragment mFragment;
Fragment homeFragment = HomeFragment.newInstance();
Fragment searchFragment = SearchFragment.newInstance();
Fragment addFragment = AddFragment.newInstance();
Fragment activityFragment = ActivityFragment.newInstance();
Fragment profileFragment = ProfileFragment.newInstance();

public MainHolderFragment() {

}

public static MainHolderFragment newInstance() {
    MainHolderFragment fragment = new MainHolderFragment();
    Bundle args = new Bundle();
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mBinder = DataBindingUtil.inflate(inflater, R.layout.fragment_main_holder, container, false);
    mFragmentManager = getChildFragmentManager();
    createFragments();
    adjustTabs();
    setUIListeners();
    return mBinder.getRoot();
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onDestroyView() {
    super.onDestroyView();
}

private void setUIListeners() {
    mBinder.bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
        @Override
        public void onTabSelected(@IdRes int tabId) {
            switch (tabId) {
                case R.id.tab_home:
                    hideShowFragment(mFragment, homeFragment);
                    mFragment = homeFragment;
                    break;
                case R.id.tab_search:
                    hideShowFragment(mFragment, searchFragment);
                    mFragment = searchFragment;
                    break;
                case R.id.tab_add:
                    hideShowFragment(mFragment, addFragment);
                    mFragment = addFragment;
                    break;
                case R.id.tab_activity:
                    hideShowFragment(mFragment, activityFragment);
                    mFragment = activityFragment;
                    break;
                case R.id.tab_profile:
                    hideShowFragment(mFragment, profileFragment);
                    mFragment = profileFragment;
                    break;
            }
        }
    });
}

//Method to add and hide all of the fragments you need to. In my case I hide 4 fragments, while 1 is visible, that is the first one.
private void addHideFragment(Fragment fragment) {
    mFragmentManager.beginTransaction().add(R.id.frame_container, fragment).hide(fragment).commit();
}

//Method to hide and show the fragment you need. It is called in the BottomBar click listener.
private void hideShowFragment(Fragment hide, Fragment show) {
    mFragmentManager.beginTransaction().hide(hide).show(show).commit();
}

//Add all the fragments that need to be added and hidden. Also, add the one that is supposed to be the starting one, that one is not hidden.
private void createFragments() {
    addHideFragment(searchFragment);
    addHideFragment(addFragment);
    addHideFragment(activityFragment);
    addHideFragment(profileFragment);
    mFragmentManager.beginTransaction().add(R.id.frame_container, homeFragment).commit();
    mFragment = homeFragment;
}

//I wanted to remove the titles and lower the icons a little bit.
private void adjustTabs() {
    mBinder.bottomBar.getTabWithId(R.id.tab_home).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_search).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_add).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_activity).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_profile).setPadding(0, 30, 0, 0);
}

All 20 comments

same question for me

I've been stuck with this for days

just to understand the question better you want the fragments to stay active when a new fragment is selected? or to work like a savedInstanceState??

exactly. something like saveInstanceState. any help would be appreciated

since you are working with fragments .addToBackStack() should work but that is more to do with how you handle your fragments rather than the roughike library.. you can read more Fragment Transaction.. hope that helps..

thanks for your suggestion, but I think using .addToBackStack needs a different architecture . am I right?

look. this is the code of my main activity (which holds fragments):

bottomBar = BottomBar.attach(this, savedInstanceState);
bottomBar.setFragmentItems(getSupportFragmentManager(), R.id.fragmentContainer,
new BottomBarFragment(new fragment1(), R.drawable.item, "item1"),
new BottomBarFragment(new fragment2.(), R.drawable.Item2, "item2"),
new BottomBarFragment(new fragment3(), R.drawable.item3, "item3"),
new BottomBarFragment(new fragment4(), R.drawable.item4, "item4"),
);
and other stuff for color and appearance...

am I missing anything ? why every time I click on each item, a new fragment is being created??

If you want them to stay active you can always use a Viewpager With a FragmentPagerAdapter or FragmentStatePagerAdapter. While this is a really easy way to set up multiple Fragments and switch between them its not perfect and you are likely to run into a couple of unexpected things. Additionally a ViewPager allows you to swipe between pages and doesn't give you a way to turn that off.

https://developer.android.com/reference/android/support/v4/view/ViewPager.html
https://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html

thanks for the help, but using a viewpager is a whole different story. I mean if I implement a viewpager then whats the point of using this library ?? isn't any better way to achieve this ?? like a saveInstanceState trick or anything ?

Well a ViewPager solves a different problem than the BottomBar library. The ViewPager provides a system for managing several fragments of content (and paging between them). BottomBar provides a UI layer for switching between several options (but not the options themselves).

In other words, a ViewPager can show you a page and help you change pages but it doesn't tell you which page you are on and BottomBar shows you which page you are on and can help you change pages but it can't show you a page.

But if you don't to use a view pager - this library looks like a promising alternative:
https://github.com/ncapdevi/FragNav/

using the "FragNav" library also didn't solve the problem ... this thing is killing me!

I managed to avoid the re-create just by hiding and showing every fragment.

Image you have three fragments (I'm using kotlin, but it's easy to understand anyways):
_val fragment1: Fragment = Fragment1()
val fragment2: Fragment = Fragment2()
val fragment3: Fragment = Fragment3()_

And before show or hide them we have to add them to a container (R.id.container that it's a FrameLayout):
_var fragment1Init: Boolean = False
var fragment2Init: Boolean = False
var fragment3Init: Boolean = False_

You can have a variable to indicate which fragment is being shown:
_val currentFragment: Fragment_

Now in the bottomBar listener we are setting ontab listener so we can know in which tab we are:
val bottomBar = find(R.id.bottomBar)
bottomBar.setOnTabClickListener(object : OnTabClickListener {
override fun onTabSelected(position: Int) {
when(position) {
0 -> {
if (!fragment1Init) {
supportFragmentManager
.beginTransaction()
.add(R.id.container, fragment1)
.commit()
fragment1Init = true
} else {
supportFragmentManager
.beginTransaction()
.hide(currentFragment)
.show(fragment1)
.commit()
}
currentFragment = fragment1
}

An just do the same for every tab, changing the add or show fragment's name.
By this way you will have control of which fragment is currently being shown, so you can just hide it to show the one you prefer, without having to re-create them every time.

This code can be really improve, like setting tags to the fragments instead of using variables, but it's a good enough workaround until you find some better solution;)

Having the same issue

I finally found a fix for this after struggling for quite a while.

  1. Initialize your fragments and create a variable for holding the active fragment
final Fragment fragment1 = new TimerFragment();
final Fragment fragment2 = new SolvesFragment();
final Fragment fragment3 = new StatsFragment();
Fragment active = fragment1;
  1. In OnCreate() add fragments to container (Note: when you add a fragment it displays it as well so I added fragment1 last to make sure it gets displayed by default when opening app.
final FragmentManager fm = getSupportFragmentManager();

fm.beginTransaction().add(R.id.main_activity, fragment3, "3").commit();
fm.beginTransaction().add(R.id.main_activity, fragment2, "2").commit();
fm.beginTransaction().add(R.id.main_activity,fragment1, "1").commit();
  1. In onMenuTabSelected() handle fragments using hide() and show()
        bottomBar.setOnMenuTabClickListener(new OnMenuTabClickListener() {
            @Override
            public void onMenuTabSelected(@IdRes int menuItemId) {
                if(menuItemId == R.id.timer_item){
                    if(active != fragment1)
                        fm.beginTransaction().show(fragment1).commit();
                    else
                        fm.beginTransaction().hide(active).show(fragment1).commit();
                    active = fragment1;
                }
                if(menuItemId == R.id.solves_item){
                    fm.beginTransaction().hide(active).show(fragment2).commit();
                    active = fragment2;
                }
                if(menuItemId == R.id.stats_item){
                    fm.beginTransaction().hide(active).show(fragment3).commit();
                    active = fragment3;
                }
            }

            @Override
            public void onMenuTabReSelected(@IdRes int menuItemId) {

            }
        });

Let me know if this works for you guys!

@jcancellier I tried your approach , it works

bottomBar.setOnMenuTabClickListener does now activate Function, in last version???

If anyone is wondering, here's my solution based on the idea of @jcancellier .

private static final String ARG_PARAM1 = "param1";

FragmentMainHolderBinding mBinder;
FragmentManager mFragmentManager;
Fragment mFragment;
Fragment homeFragment = HomeFragment.newInstance();
Fragment searchFragment = SearchFragment.newInstance();
Fragment addFragment = AddFragment.newInstance();
Fragment activityFragment = ActivityFragment.newInstance();
Fragment profileFragment = ProfileFragment.newInstance();

public MainHolderFragment() {

}

public static MainHolderFragment newInstance() {
    MainHolderFragment fragment = new MainHolderFragment();
    Bundle args = new Bundle();
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mBinder = DataBindingUtil.inflate(inflater, R.layout.fragment_main_holder, container, false);
    mFragmentManager = getChildFragmentManager();
    createFragments();
    adjustTabs();
    setUIListeners();
    return mBinder.getRoot();
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onDestroyView() {
    super.onDestroyView();
}

private void setUIListeners() {
    mBinder.bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
        @Override
        public void onTabSelected(@IdRes int tabId) {
            switch (tabId) {
                case R.id.tab_home:
                    hideShowFragment(mFragment, homeFragment);
                    mFragment = homeFragment;
                    break;
                case R.id.tab_search:
                    hideShowFragment(mFragment, searchFragment);
                    mFragment = searchFragment;
                    break;
                case R.id.tab_add:
                    hideShowFragment(mFragment, addFragment);
                    mFragment = addFragment;
                    break;
                case R.id.tab_activity:
                    hideShowFragment(mFragment, activityFragment);
                    mFragment = activityFragment;
                    break;
                case R.id.tab_profile:
                    hideShowFragment(mFragment, profileFragment);
                    mFragment = profileFragment;
                    break;
            }
        }
    });
}

//Method to add and hide all of the fragments you need to. In my case I hide 4 fragments, while 1 is visible, that is the first one.
private void addHideFragment(Fragment fragment) {
    mFragmentManager.beginTransaction().add(R.id.frame_container, fragment).hide(fragment).commit();
}

//Method to hide and show the fragment you need. It is called in the BottomBar click listener.
private void hideShowFragment(Fragment hide, Fragment show) {
    mFragmentManager.beginTransaction().hide(hide).show(show).commit();
}

//Add all the fragments that need to be added and hidden. Also, add the one that is supposed to be the starting one, that one is not hidden.
private void createFragments() {
    addHideFragment(searchFragment);
    addHideFragment(addFragment);
    addHideFragment(activityFragment);
    addHideFragment(profileFragment);
    mFragmentManager.beginTransaction().add(R.id.frame_container, homeFragment).commit();
    mFragment = homeFragment;
}

//I wanted to remove the titles and lower the icons a little bit.
private void adjustTabs() {
    mBinder.bottomBar.getTabWithId(R.id.tab_home).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_search).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_add).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_activity).setPadding(0, 30, 0, 0);
    mBinder.bottomBar.getTabWithId(R.id.tab_profile).setPadding(0, 30, 0, 0);
}

If i understand correctly this issue has nothing to do with the library, but, rather people not knowing how to work with Fragments, i'm closing it for now, if someone feels that there is something the library should be doing re-open the issue.

@sunil-singh-chaudhary what I'm I supposed to see there? I still think the issue is people don't know how to deal with fragments.

i have solve the problem there

Was this page helpful?
0 / 5 - 0 ratings