web-dev-qa-db-fra.com

Fragment recréé sur l'élément de vue de navigation inférieur sélectionné

Voici mon code pour l'élément de vue de navigation inférieure sélectionné

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {  
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Fragment fragment = null;
    switch (item.getItemId()) {
        case R.id.action_one:
            // Switch to page one
            fragment = FragmentA.newInstance();
            break;
        case R.id.action_two:
            // Switch to page two
            fragment = FragmentB.newInstance();
            break;
        case R.id.action_three:
            // Switch to page three
            fragment = FragmentC.newInstance();
            break;
    }
    getSupportFragmentManager().beginTransaction().replace(R.id.container,fragment,"TAG").commit();
    return true;
}
});

Maintenant, mon problème est que chaque fois qu'un fragment est recréé, je ne veux pas que le fragment soit recréé à chaque fois. J'ai aussi essayé d'ajouter addToBackStack (null), mais cette fois-ci, une pression sur le bouton retour continue de faire éclater des fragments que je ne souhaite pas.

Est-il possible d'afficher des fragments dans la barre de navigation inférieure sélectionnée sans recréer un fragment

28
amodkanthe

Avec la bibliothèque de support v26, vous pouvez le faire

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

Fragment curFrag = mFragmentManager.getPrimaryNavigationFragment();
if (curFrag != null) {
    fragmentTransaction.detach(curFrag);
}

Fragment fragment = mFragmentManager.findFragmentByTag(tag);
if (fragment == null) {
    fragment = new YourFragment();
    fragmentTransaction.add(container.getId(), fragment, tag);
} else {
    fragmentTransaction.attach(fragment);
}

fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();
18
Viven

Soyez prudent lorsque vous utilisez replace. Même si vous fournissez un fragment qui existe déjà en mémoire, replace relancera le cycle de vie du fragment. Pour éviter un redémarrage, les méthodes de l'objet transaction incluent add, show et hide, qui peuvent être utilisées pour afficher le fragment correct sans le redémarrer.

private fun switchFragment(index: Int) {
    val transaction = supportFragmentManager.beginTransaction()
    val tag = fragments[index].tag

    // if the fragment has not yet been added to the container, add it first
    if (supportFragmentManager.findFragmentByTag(tag) == null) {
        transaction.add(R.id.container, fragments[index], tag)
    }

    transaction.hide(fragments[navigationBar.currentTabPosition])
    transaction.show(fragments[index])
    transaction.commit()
}
5
Maxwell

J'ai fait face au même problème et finalement j'ai trouvé la solution, vous pouvez essayer ce code. c'est un travail pour moi.

import Android.support.annotation.NonNull;
import Android.support.design.widget.BottomNavigationView;
import Android.support.v4.app.Fragment;
import Android.support.v4.app.FragmentManager;
import Android.support.v4.app.FragmentTransaction;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.view.MenuItem;
import Android.widget.FrameLayout;
import Android.widget.Toast;

public class MainActivity extends AppCompatActivity {
public BottomNavigationView bv;
public home_fragment home;
public account_fragment afrag;
public other_fragment other;
public FrameLayout fr;
Android.support.v4.app.Fragment current;
//public FragmentTransaction frt;
    public static int temp=0;
    final Fragment fragment11 = new account_fragment();
    final Fragment fragment22 = new home_fragment();
    final Fragment fragment33 = new other_fragment();
    final FragmentManager fm = getSupportFragmentManager();
    Fragment active = fragment11;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bv=(BottomNavigationView) findViewById(R.id.navigationView);


        fm.beginTransaction().add(R.id.main_frame, fragment33, "3").hide(fragment33).commit();
        fm.beginTransaction().add(R.id.main_frame, fragment22, "2").hide(fragment22).commit();
        fm.beginTransaction().add(R.id.main_frame,fragment11, "1").commit();
bv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.account:
                        fm.beginTransaction().hide(active).show(fragment11).commit();
                        active = fragment11;
                        return true;

                    case R.id.home1:
                        fm.beginTransaction().hide(active).show(fragment22).commit();
                        active = fragment22;
                        return true;

                    case R.id.other:
                        fm.beginTransaction().hide(active).show(fragment33).commit();
                        active = fragment33;
                        return true;
                }
                return false;
            }
        });
      bv.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
          @Override
          public void onNavigationItemReselected(@NonNull MenuItem item) {
              Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();

          }
      });


    }

}
4
Mazen Jameel

Essaye ça :

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    Fragment fragment = null;
                    Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
                    switch (item.getItemId()) {
                        case R.id.action_one:
                            // Switch to page one
                            if (!(currentFragment instanceof FragmentA)) {
                                fragment = FragmentA.newInstance();
                            }
                            break;
                        case R.id.action_two:
                            // Switch to page two
                            if (!(currentFragment instanceof FragmentB)) {
                                fragment = FragmentB.newInstance();
                            }
                            break;
                        case R.id.action_three:
                            // Switch to page three
                            if (!(currentFragment instanceof FragmentC)) {
                                fragment = FragmentC.newInstance();
                            }
                            break;
                    }
                    getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "TAG").commit();
                    return true;
                }
            });

Cela aura le fragment actuel dans votre container et si vous cliquez à nouveau sur ce fragment, le rajout n’aura pas lieu.

4
Raphael Teyssandier

setOnNavigationItemReselectedListener serait une meilleure solution pour cela 

3
akaMahesh

Utilisez setOnNavigationItemReselectedListener comme ceci:

private BottomNavigationView.OnNavigationItemReselectedListener onNavigationItemReselectedListener
            = new BottomNavigationView.OnNavigationItemReselectedListener() {

        @Override
        public void onNavigationItemReselected(@NonNull MenuItem item) {
            Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();
        }
    };

et appelez-le en utilisant:

navigation.setOnNavigationItemReselectedListener(onNavigationItemReselectedListener);
3
Pb Studies

Cela semblait bien fonctionner pour moi. Au lieu d'attacher et de détacher, j'utilise show ou hide pour maintenir l'état de fragment.

public void changeFragment(Fragment fragment, String tagFragmentName) {

        FragmentManager mFragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

        Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
        if (currentFragment != null) {
            fragmentTransaction.hide(currentFragment);
        }

        Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
        if (fragmentTemp == null) {
            fragmentTemp = fragment;
            fragmentTransaction.add(R.id.frame_layout, fragmentTemp, tagFragmentName);
        } else {
            fragmentTransaction.show(fragmentTemp);
        }

        fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
        fragmentTransaction.setReorderingAllowed(true);
        fragmentTransaction.commitNowAllowingStateLoss();
    }

Et c'est comme ça que je l'utilise

 private void initViews() {
        BottomNavigationView bottomNavigationView = findViewById(R.id.navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener
                (new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        Fragment selectedFragment = null;
                        switch (item.getItemId()) {
                            case R.id.explore:
                                changeFragment(new ExploreFragment(), ExploreFragment.class
                                        .getSimpleName());
                                toggleViews(true, "");
                                break;
                            case R.id.favorite:
                                changeFragment(new FavoriteFragment(), FavoriteFragment.class
                                        .getSimpleName());
                                toggleViews(false, "Favorites");
                                break;
                            case R.id.venue:
                                changeFragment(new VenueFragment(), VenueFragment.class.getSimpleName());
                                toggleViews(false, "Venues");
                                break;
                            case R.id.profile:
                                changeFragment(new ProfileFragment(), ProfileFragment.class
                                        .getSimpleName());
                                toggleViews(false, "Profile");
                                break;
                        }
                        return true;
                    }
                });

        //Manually displaying the first fragment - one time only
        changeFragment(new ExploreFragment(), ExploreFragment.class
                .getSimpleName());

    }

2
Steve Kamau

J'ai résolu ce problème en ajoutant une ViewPager à laquelle j'ai délégué tous mes fragments de navigation. Son adaptateur (FragmentPagerAdapter) ne recrée pas les instances de fragments lorsque l'utilisateur navigue dans BotoomNavigationView.

Pour ce faire, vous devez suivre 5 étapes simples:

  1. ajoutez une ViewPager à votre mise en page;
  2. implémenter son adaptateur:

    class YourNavigationViewPagerAdapter(fm: FragmentManager,
                                         private val param1: Int,
                                         private val param2: Int)
    
        : FragmentPagerAdapter(fm) {
    
        override fun getItem(p0: Int) = when(p0) {
            0 -> NavigationFragment1.newInstance(param1, param2)
            1 -> NavigationFragment2.newInstance(param1, param2)
            2 -> NavigationFragment3.newInstance(param1, param2)
            else -> null
        }
    
        override fun getCount() = 3
    }
    
  3. n'oubliez pas de définir le nouvel adaptateur:

    yourViewPager.adapter = YourNavigationViewPagerAdapter(supportFragmentManager, param1, param2)
    
  4. définissez une OnNavigationItemSelectedListener sur votre BottomNavigationView comme suit:

    yourBottomNavigationView.setOnNavigationItemSelectedListener {
    
        when(it.itemId) {
    
            R.id.yourFirstFragmentMenuItem -> {
                yourViewPager.currentItem = 0
                true
            }
    
            R.id.yourSecondFragmentMenuItem -> {
                yourViewPager.currentItem = 1
                true
            }
    
            R.id.yourThirdFragmentMenuItem -> {
                yourViewPager.currentItem = 2
                true
            }
    
    
            else -> false
        }
    }
    
  5. définissez une OnPageChangeListener sur votre ViewPager comme suit:

    yourViewPager.addOnPageChangeListener(object : 
    ViewPager.OnPageChangeListener {
        override fun onPageScrollStateChanged(p0: Int) {
    
        }
    
        override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
    
        }
    
        override fun onPageSelected(p0: Int) {
            yourBottomNavigationView.menu.getItem(p0).isChecked = true
        }
    
    })
    
  6. prendre plaisir :)

1
ivan8m8

Il y a plusieurs cas de test impliqués dans la navigation correcte, je colle mon code avec tous les cas de test vérifiés.

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        switch (item.getItemId()) {
            case R.id.dashboard:
                Fragment fragment;
                fragment = fragmentManager.findFragmentByTag(DashboardFragment.TAG);

                if (fragment == null) {
                    fragment = new DashboardFragment();
                    fragmentTransaction.add(R.id.frame, fragment, DashboardFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.expenses:
                fragment = fragmentManager.findFragmentByTag(ExpenseFragment.TAG);
                if (fragment == null) {
                    fragment = new ExpenseFragment();
                    fragmentTransaction.add(R.id.frame, fragment, ExpenseFragment.TAG);
                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.vehicle_parts:
                Bundle bundle = new Bundle();
                bundle.putInt("odometer", vehicle.getOdometer());
                bundle.putString("vehicle_id", vehicle.get_id());
                fragment = fragmentManager.findFragmentByTag(PartsFragment.TAG);
                if (fragment == null) {
                    fragment = new PartsFragment();
                    fragment.setArguments(bundle);
                    fragmentTransaction.add(R.id.frame, fragment, PartsFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
            case R.id.blog:
                fragment = fragmentManager.findFragmentByTag(BlogFragment.TAG);

                if (fragment == null) {
                    fragment = new BlogFragment();
                    fragmentTransaction.add(R.id.frame, fragment, BlogFragment.TAG);

                } else {
                    fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
                    fragmentTransaction.attach(fragment);
                }
                fragmentTransaction.setPrimaryNavigationFragment(fragment);
                fragmentTransaction.commitNow();
                return true;
        }
        return false;
0
HarshitG

J'ai amélioré les réponses de @ Viven et l'ai écrit avec Kotlin. Ma version instancie fragment pour la première fois seulement, cache/montre. Je suis nouveau chez Kotlin, alors dites-moi si je peux améliorer quelque chose.

Nous devons avoir un identifiant pour taguer la carte:

private val fragmentTags = hashMapOf(
        R.id.action_home to "home_fragment",
        R.id.action_profile to "profile_fragment"
)

Le code de l'auditeur:

bottomNavigation.run {
    setOnNavigationItemSelectedListener { menuItem ->
        supportFragmentManager.beginTransaction()
                .let { transaction ->
                    // hide current fragment
                    supportFragmentManager.primaryNavigationFragment?.let {
                        // if selected fragment's tag is same, do nothing.
                        if (it.tag == fragmentTags[menuItem.itemId]) {
                            return@setOnNavigationItemSelectedListener true
                        }

                        transaction.hide(it)
                    }

                    var fragment  = supportFragmentManager.findFragmentByTag(fragmentTags[menuItem.itemId])

                    if (fragment == null) {
                        // instantiate fragment for the first time
                        fragment = when(menuItem.itemId) {
                            R.id.action_home -> HomeFragment()
                            R.id.action_profile -> ProfileFragment()
                            else -> null
                        }?.also {
                            // and add it 
                            transaction.add(R.id.frame, it, fragmentTags[menuItem.itemId])
                        }
                    } else {
                        // if it's found show it 
                        transaction.show(fragment)
                    }


                    transaction
                            .setPrimaryNavigationFragment(fragment)
                            .setReorderingAllowed(true)
                }.commitNowAllowingStateLoss()

        return@setOnNavigationItemSelectedListener true
    }

    //bottomNavigation things
    itemIconTintList = null
    selectedItemId = R.id.action_home
}
0
osrl