Search code examples
javaandroidandroid-fragmentsandroid-viewpager2

Swipe between fragments with help of BottomNavigationBar


I have trouble finding a good example on how to swipe between fragments with help of bottom navigatiom bar. Since FragmentStatePagerAdapter is deprecated and a new ViewPager2 is now recommended instead of the old ViewPager I want to use ViewPager2 and FragmentStateAdapter in my code instead. I have found an example of how to combine BottomNavigationBar and ViewPager here and I want to do something similar. My code have many similarities to the one in the example with the only difference that I have my code in a fragment instead of an activity. Here is a picture of how my FrontendFragment display look like. I can switch between the views using the bottomnavigationbar but I also want to be able to swipe between the views. Can someone help me or at least direct me on the right way? Here is my code:

FragmentPagerAdapter Class:

public class FragmentPagerAdapter extends FragmentStateAdapter {

    private static final int mFragmentCount = 5;

    public FragmentPagerAdapter(@NonNull Fragment fragment) {
        super(fragment);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position){

            case 0:
                return new HomeFragment();
            case 1:
                return new SearchFragment();
            case 2:
                return new AddFragment();
            case 3:
                return new MessageFragment();
            case 4:
                return new ProfileFragment();
        }
        return null;
    }

    @Override
    public int getItemCount() {
        return mFragmentCount;
    }
} 

FrontendFragment Class:

public class FrontendFragment extends Fragment implements BottomNavigationView.OnNavigationItemSelectedListener{

    private BottomNavigationView mBottomNavigationView;
    private ViewPager2 mViewPager2;
    private FragmentPagerAdapter mFragmentPagerAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v =  inflater.inflate(R.layout.fragment_frontend, container, false);

        loadFragment(new HomeFragment());

        mBottomNavigationView = v.findViewById(R.id.bottomNavigationBar);
        mBottomNavigationView.setOnNavigationItemSelectedListener(this);


        return v;
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        Fragment selectedFragment = null;

        switch (item.getItemId()) {
            case R.id.home_icon:
                selectedFragment = new HomeFragment();
                break;
            case R.id.search_icon:
                selectedFragment = new SearchFragment();
                break;
            case R.id.add_icon:
                selectedFragment = new AddFragment();
                break;

            case R.id.message_icon:
                selectedFragment = new MessageFragment();
                break;

            case R.id.profile_icon:
                selectedFragment = new ProfileFragment();
                break;
        }
        
        return loadFragment(selectedFragment);
    }


    private boolean loadFragment(Fragment selectedFragment) {

        if(selectedFragment != null){

            MainActivity.sFm.beginTransaction().replace(R.id.relLayoutMiddle, selectedFragment).commit();
            return true;
        }
        return false;
    }
}

Thanks in advance!


Solution

  • As I'm already using BottomNav with ViewPager2 in one of my app, I can help.

    Your code is partially correct which means your FragmentPagerAdapter is fine, but not your FrontEndFragment.

    See, the FragmentPagerAdapter has to be set to a ViewPager2 as

    //this here is the FrontEndFragment 
    mViewPager2.setAdapter(new FragmentPagerAdapter(this));
    

    Then, You don't have to do the FragmentTransaction at all, you just have to change the ViewPager2's current item position through the BottomNavigationBar as

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    
        switch (item.getItemId()) {
            case R.id.home_icon:
                mViewPager2.setCurrentItem(0);
                break;
            case R.id.search_icon:
                mViewPager2.setCurrentItem(1);
                break;
            case R.id.add_icon:
                mViewPager2.setCurrentItem(2);
                break;
    
            case R.id.message_icon:
                mViewPager2.setCurrentItem(3);
                break;
    
            case R.id.profile_icon:
                mViewPager2.setCurrentItem(4);
                break;
        }
        
        return false;
    }
    

    This is all, you don't have to deal with Fragments at all apart from the FragmentPagerAdapter. Also, don't forget to remove the loadFragment(new HomeFragment()); which is not required, nor the function loadFragment() is required.

    (Optional), Furthermore, if you want to disable the Swipe Action of the ViewPager2 and want the Fragments to be selected based on the Selected BottomNav item only, then you can set setUserInputEnabled() property of ViewPager2 as false.


    Next, to set the BottomNavigationBar's item as selected based on the swipe of the ViewPager2, what you've to do is,

    Create a global var

    MenuItem previousMenuItem;
    

    Then, set a default item (first) to be selected of BottomNav on activity start as

    mBottomNavigationView.getMenu().getItem(0).setChecked(true);
    

    Finally, set an OnPageSelected() callback on ViewPager2 to update the selected Menu Item as:

    mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }
    
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            if (previousMenuItem != null) {
                previousMenuItem.setChecked(false);
            }
            else {
                mBottomNavigationView.getMenu().getItem(0).setChecked(false);
            }
            mBottomNavigationView.getMenu().getItem(position).setChecked(true);
            previousMenuItem = mBottomNavigationView.getMenu().getItem(position);
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
        }
    });
    

    What you're doing here is that you're setting default item to previousMenuItem and then on swiping to a different page, deselecting the previousMenuItem and selecting the new one, which means updating the BottomNav based on ViewPager's current item. This is the complete code you require to acheive your objective.