web-dev-qa-db-fra.com

Comment gérer la navigation de fond parfaitement avec le dos appuyé

Je travaille sur une barre de navigation inférieure, mais la barre de navigation inférieure ne me convient pas parfaitement.

Ma classe MainActivity:

public class MainActivity extends AppCompatActivity {

    private static final String SELECTED_ITEM = "selected_item";

    private BottomNavigationView bottomNavigationView;
    private Toolbar toolbar;
    private MenuItem menuItemSelected;
    private int mMenuItemSelected;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                selectFragment(item);
                return true;
            }
        });

        //Always load first fragment as default
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.frameLayout, new AnnouncementFragment());
        fragmentTransaction.commit();

        if (savedInstanceState != null) {
            mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
            menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
        } else {
            menuItemSelected = bottomNavigationView.getMenu().getItem(0);
        }

        selectFragment(menuItemSelected);
    }

    private void selectFragment(MenuItem item) {
        Fragment fragment = null;
        Class fragmentClass;
        switch (item.getItemId()) {
            case R.id.action_announcement:
                fragmentClass = AnnouncementFragment.class;
                break;
            case R.id.action_menu:
                fragmentClass = MenuFragment.class;
                break;
            case R.id.action_menu_reports:
                fragmentClass = ReportFragment.class;
                break;
            case R.id.action_setting:
                fragmentClass = SettingFragment.class;
                break;

            default:
                fragmentClass = AnnouncementFragment.class;
        }

        try {
            fragment = (Fragment) fragmentClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.frameLayout, fragment).commit();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt(SELECTED_ITEM, mMenuItemSelected);
        super.onSaveInstanceState(outState);
    }

Et mon dos pressé aussi ne fonctionne pas correctement:

 @Override
    public void onBackPressed() {
        MenuItem homeItem = bottomNavigationView.getMenu().getItem(0);
        if (mMenuItemSelected != homeItem.getItemId()) {
            selectFragment(homeItem);
        } else {
            super.onBackPressed();
        }
    }

Comment devrais-je faire cela parce que le menu du bas a une distribution inégale sur le bar. Comment bien entretenir l'espace du menu sans distribution inégale.

Je joins ici le résultat obtenu sur AVD  First I

 Another Selection

11
Venky

Selon les directives pour Conception du matériel

Sur Android, le bouton Précédent ne permet pas de naviguer entre le bas vues de la barre de navigation.

EDIT: le lien Material Design ne mentionne plus le comportement du bouton Précédent.

En appuyant sur le bouton Précédent, vous pouvez quitter l'application, qui est le comportement par défaut, comme dans Google Photo ...

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
// note: there is NOT a addToBackStack call
fragmentTransaction.commit();

... ou conduisez l'utilisateur à la section home puis, si on le pousse à nouveau, à la sortie.

Personnellement je trouve ce dernier modèle beaucoup mieux.

Pour l'obtenir sans remplacer onBackPressed, vous devez identifier le fragment home et le différencier de all les autres

navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.navigation_home:
                viewFragment(new HomeFragment(), FRAGMENT_HOME);
                return true;
            case R.id.navigation_page_1:
                viewFragment(new OneFragment(), FRAGMENT_OTHER);
                return true;
            case R.id.navigation_page_2:
                viewFragment(new TwoFragment(), FRAGMENT_OTHER);
                return true;
        }
        return false;
    }
});

Ce que vous devez faire maintenant, c'est écrire la méthode viewfragment qui doit:

  1. Savoir combien de fragments il y a dans la pile avant le commit
  2. Si le fragment est not "type maison", enregistrez-le sur la pile avant La commit

  3. Ajoutez une OnBackStackChangedListener qui, lorsque la pile diminue, (C’est-à-dire lorsque j’appuie de nouveau), supprime tous les fragments qui sont pas "type de maison" ( POP_BACK_STACK_INCLUSIVE ), nous amenant au fragment de maison

Ci-dessous la méthode complète avec commentaires

private void viewFragment(Fragment fragment, String name){
    final FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.replace(R.id.content, fragment);
    // 1. Know how many fragments there are in the stack
    final int count = fragmentManager.getBackStackEntryCount();
    // 2. If the fragment is **not** "home type", save it to the stack
    if( name.equals( FRAGMENT_OTHER) ) {
        fragmentTransaction.addToBackStack(name);
    }
    // Commit !
    fragmentTransaction.commit();
    // 3. After the commit, if the fragment is not an "home type" the back stack is changed, triggering the
    // OnBackStackChanged callback
    fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            // If the stack decreases it means I clicked the back button
            if( fragmentManager.getBackStackEntryCount() <= count){
                // pop all the fragment and remove the listener
                fragmentManager.popBackStack(FRAGMENT_OTHER, POP_BACK_STACK_INCLUSIVE);
                fragmentManager.removeOnBackStackChangedListener(this);
                // set the home button selected
                navigation.getMenu().getItem(0).setChecked(true);
            }
        }
    });
}
9
marco

Essaye ça 

@Override
    public void onBackPressed() {
        BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
        int seletedItemId = bottomNavigationView.getSelectedItemId();
        if (R.id.home != seletedItemId) {
            setHomeItem(MainActivity.this);
        } else {
            super.onBackPressed();
        }
    }

public static void setHomeItem(Activity activity) {
    BottomNavigationView bottomNavigationView = (BottomNavigationView)
            activity.findViewById(R.id.navigation);
    bottomNavigationView.setSelectedItemId(R.id.home);
}
9
Shailesh Bandil
@Override
    public void onBackPressed() {
        BottomNavigationView mBottomNavigationView = findViewById(R.id.navigation);
        if (mBottomNavigationView.getSelectedItemId() == R.id.navigation_home)
        {
            super.onBackPressed();
            finish();
        }
        else
        {
            mBottomNavigationView.setSelectedItemId(R.id.navigation_home);
        }
    }
5
Vasu

onBackPressed n'a pas fonctionné pour moi. Donc cela j'ai utilisé.

 @Override
 protected void onResume() {
    super.onResume();
    bottomNavigationView.getMenu().getItem(0).setChecked(true);
 }
4
Thriveni

C'est peut-être un peu tard, mais je pense que la meilleure façon de le faire est aussi simple que cela.

 @Override
public void onBackPressed() {
    if (mBottomNavigationView.getSelectedItemId() == R.id.action_home) {
        super.onBackPressed();
    } else {
        mBottomNavigationView.setSelectedItemId(R.id.action_home);
    }
}

J'espère que ça aide et bon codage :)

3
Fahd Al-Qahtani

La meilleure façon de procéder est la suivante: lorsque vous êtes dans le bouton d'accueil, l'application est fermée et lorsqu'un autre bouton est dans le bouton d'accueil.

Ci-dessous je mets mon code:

d'abord, je charge le bouton d'accueil dans la navigation

private void loadFragment(Fragment fragment) {
    Toast.makeText(this, "load", Toast.LENGTH_SHORT).show();
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.frame_container, fragment, TAG_FRAGMENT);
    transaction.commit();
}

et rappelez-vous ne pas appeler addToBackStack() . et ensuite gérer la situation par onBackPressed():

@Override
public void onBackPressed() {
    if (navigation.getSelectedItemId() == R.id.bottomAkhbar) {
        super.onBackPressed();
    } else {
        navigation.setSelectedItemId(R.id.bottomAkhbar);
    }
}
2
ali hoseinpoor

Je faisais face au même problème mais après cela, j’ai eu la solution

D'abord, collez ce code dans votre activité principale (où vous utilisez la barre de navigation inférieure)

BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);

Et puis créez une classe nommée BottomNavigationViewHelper et collez le code suivant.

public class BottomNavigationViewHelper {
public static void disableShiftMode(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);
            //noinspection RestrictedApi
            item.setShiftingMode(false);
            // set once again checked value, so view will be updated
            //noinspection RestrictedApi
            item.setChecked(item.getItemData().isChecked());
        }
    } catch (NoSuchFieldException e) {
        Log.e("BNVHelper", "Unable to get shift mode field", e);
    } catch (IllegalAccessException e) {
        Log.e("BNVHelper", "Unable to change value of shift mode", e);
    }
}

}

J'espère que ça aide

1
Rajat Mittal

J'ai fait cela après avoir tout essayé et finalement, cela a fonctionné -_-. J'ai collé cette méthode de substitution 2 dans chacune des activités que je surfe dans la navigation inférieure.

  @Override
protected void onResume() {
    super.onResume();
    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
    bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);

}

@Override
protected void onRestart() {
    super.onRestart();
    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
    bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);
}
1
Ananna Rahman

Réponse tardive mais ça y est.

Supposons que vous avez un BottomNavigation inside MainActivity avec 4 Fragments

  1. FragmentA 
  2. FragmentB 
  3. FragmentC 
  4. FragmentD

Si vous ajoutez chaque fragment au backstack, procédez comme suit:

Avec kotlin:

 main_bottom_navigation.setOnNavigationItemSelectedListener { item ->
        var fragment: Fragment? = null
        when (item.itemId) {
            R.id.nav_a -> fragment = FragmentA()
            R.id.nav_b -> fragment = FragmentB()
            R.id.nav_c -> fragment = FragmentC()
            R.id.nav_d -> fragment = FragmentD()
        }

        supportFragmentManager
            .beginTransaction()
            .setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
            .replace(R.id.home_content, fragment!!)
            .addToBackStack(fragment.tag)
            .commit()

        true
    }

Vous n'avez pas vraiment besoin de remplacer onBackPressed () à l'intérieur de MainActivity mais explicitement lancé sur chaque fragment et assigné le BottomNavigation comme ceci:

FragmentA.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(0).isChecked = true
}

FragmentB.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(1).isChecked = true
}

FragmentC.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(2).isChecked = true
}

FragmentD.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(3).isChecked = true
}

De cette manière, le fragment de pile restituera et même quittera l'application lorsqu'il atteindra 0.

1
Ispam

Pour ce qui est de votre besoin, vous utiliseriez des fragments de navigation, vous pouvez utiliser Tablayout avec un pageur de vue et effectuer une navigation de fond.

<Android.support.design.widget.TabLayout
        Android:id="@+id/tab_layout"
        Android:layout_width="match_parent"
        Android:layout_height="60dp"></Android.support.design.widget.TabLayout>

puis configurez viewpager avec la disposition des onglets et ajoutez une icône à tablayout dans votre activité

tabLayout = (TabLayout) findViewById(R.id.tab_layout);
    viewPager = (ViewPager) findViewById(R.id.controller_pager);
    viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
    viewPager.setOffscreenPageLimit(4);
    tabLayout.setupWithViewPager(viewPager);
    tabLayout.getTabAt(0).setIcon(R.drawable.selector_home);
    tabLayout.getTabAt(1).setIcon(R.drawable.selector_contact);
    tabLayout.getTabAt(2).setIcon(R.drawable.selector_profile);
    tabLayout.getTabAt(3).setIcon(R.drawable.selector_settings);

maintenant, gérez tout sur le clic de tablayout et cela fonctionnera bien. tabLayout.addOnTabSelectedListener (this);

1
pankaj yadav

Utilisez addToBackStack Method pour appeler un fragment comme celui-ci,

   getSupportFragmentManager().beginTransaction().addToBackStack(null).add(R.id.content_home_nav,newFragment).commit();

Utilisez ce code dans votre méthode onBackPressed

if (getSupportFragmentManager().getBackStackEntryCount() > 0 ){
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
1
scienticious

S'il vous plaît essayer cette solution . J'ai apporté des modifications dans votre code donné en question.

J'ai supposé que lorsque vous appuierez pour la première fois, votre application reviendra au fragment d'origine (dans votre fragment d'annonce) et si vous appuyez à nouveau, l'application se fermera.

Ce flux sera également reflété dans la barre de navigation inférieure.

public class MainActivity extends AppCompatActivity {
private static final String BACK_STACK_ROOT_TAG = "root_home_fragment";
private static final String SELECTED_ITEM = "selected_item";
private Fragment fragment;
private FragmentManager fragmentManager;
private BottomNavigationView bottomNavigationView;
private Toolbar toolbar;
private MenuItem menuItemSelected;
private int mMenuItemSelected;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() {

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        boolean selected = false;
        switch (item.getItemId()) {
            case R.id.action_announcement:
                fragment = AnnouncementFragment.newInstance();
                selected =  true;
                break;
            case R.id.action_menu:
                fragment = MenuFragment.newInstance();
                selected =  true;
                break;
            case R.id.action_menu_reports:
                fragment = ReportFragment.newInstance();
                selected =  true;
                break;
        case R.id.action_setting:
        fragment = SettingFragment.newInstance();
                selected =  true;

        }
        if(fragment !=null){
            fragmentManager = getFragmentManager();
            switch (item.getItemId()) {
              case R.id.action_announcement:
                   // Pop every fragment from backstack including home fragment.
                   fragmentManager.popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                   fragmentManager.beginTransaction()
                            .replace(R.id.content, fragment)
                            .addToBackStack(BACK_STACK_ROOT_TAG)
                            .commit();
                    break;
               default: 

                   fragmentManager.beginTransaction()
                            .replace(R.id.content, fragment)
                            .addToBackStack(null)
                            .commit();  
                   break;
           }
        }
        return selected;
    }

};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
    bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

    //Always load first fragment as default
    bottomNavigationView.setSelectedItemId(R.id.action_announcement);

    if (savedInstanceState != null) {
        mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
        menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
    } else {
        menuItemSelected = bottomNavigationView.getMenu().getItem(0);
    }
}



@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt(SELECTED_ITEM, mMenuItemSelected);
    super.onSaveInstanceState(outState);
}

public void onBackPressed() {
    int count = getFragmentManager().getBackStackEntryCount();
    if(count >1){
        // We have lots of fragment on backstack to be popped.
        // Pop till the root fragment.
        getFragmentManager().popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
     bottomNavigationView.setSelectedItemId(R.id.action_announcement);
    }
    else{
        // Close the application when we are on home fragment.
        supportFinishAfterTransition();
    }
}
}
1
Tony

J'avais le même problème et j'ai donc résolu le problème en utilisant cette méthode: Dans mon activité principale, j'ai une barre de navigation inférieure et un tiroir de navigation, je dois synchroniser les éléments de mon tiroir et de la navigation inférieure:

J'ai créé une méthode pour mon fragment principal et les autres: Mon fragment de remplacement principal:

public void MainFragmentChanger(final Fragment fragment, final String TAG){
    if (main_page_fragment != null){
        fragmentTransaction = myFragmentManager.beginTransaction();
        fragmentTransaction.remove(main_page_fragment).commit();
    }
    if (main_drawer.isDrawerOpen()){
        main_drawer.closeDrawer();
    }
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            main_page_fragment = fragment;
            main_page_fragment.setRetainInstance(true);
            fragmentTransaction = myFragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.main_container, main_page_fragment,TAG);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            fragmentTransaction.commitAllowingStateLoss();
        }
    });
}

et ceci est pour mon autre fragment de remplacement:

public void changeBottomFragment(final Fragment fragment, final String TAG){
    if (main_drawer.isDrawerOpen()){
        main_drawer.closeDrawer();
    }
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            main_page_fragment = fragment;
            main_page_fragment.setRetainInstance(true);
            fragmentTransaction = myFragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.main_container, main_page_fragment);
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            fragmentTransaction.commitAllowingStateLoss();
        }
    });
}

donc, après cela, je dois synchroniser les éléments dans le tiroir et dans la barre: :

public void changeNavAndBarStats(String tag){
    if (tag == "flash"){
        bottomNavigationBar.selectTab(2,false);
        main_drawer.setSelection(flashcards.getIdentifier(),false);
    }else if (tag == "dic"){
        bottomNavigationBar.selectTab(3,false);
        main_drawer.setSelection(dictionary.getIdentifier(),false);
    }else if (tag == "Home"){
        bottomNavigationBar.selectTab(0,false);
        main_drawer.setSelection(home.getIdentifier(),false);
    }
}

Alors j'appelle mes fragments comme ça:

MainFragmentChanger(new MainPageFragment(),"Home");
                            bottomNavigationBar.selectTab(0,false);

changeBottomFragment(new FlashCardFragment(),"flash");
                            bottomNavigationBar.selectTab(2,false);

changeBottomFragment(new TranslateFragment(),"dic");
                            bottomNavigationBar.selectTab(3,false);

A la fin, j'appelle changeNavAndBarStatus dans la méthode onResume de mon fragment:

((MainPageActivity)getContext()).changeNavAndBarStats("flash");

C'est tout! tu es prêt!

1
MoeinDeveloper

La plupart du temps, lorsque vous appuyez sur le bouton Précédent, l'ancien fragment de la pile arrière, donc l'appel système cette méthode onCreateView ()

il ajoute ce code

val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.activity_main_bottom_navigation)
    bottomNav?.selectedItemId = R.id.the_id_of_the_icon__that_represent_the_fragment
1
Declan Nnadozie

Essaye ça.

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {

            // Gets the previous global stack count
            int previousStackCount = mPreviousStackCount;

            // Gets a FragmentManager instance
            FragmentManager localFragmentManager = getSupportFragmentManager();

            // Sets the current back stack count
            int currentStackCount = localFragmentManager.getBackStackEntryCount();

            // Re-sets the global stack count to be the current count
            mPreviousStackCount = currentStackCount;

            boolean popping = currentStackCount < previousStackCount;

            if(popping){
                bottomNavigationView.getMenu().getItem(0).setChecked(true);
            }

        }
    });
1
bagyn