Листайте между фрагментами с помощью BottomNavigationBar

Мне сложно найти хороший пример того, как пролистывать fragments с помощью bottom navigatiom bar. Поскольку FragmentStatePagerAdapter устарел, а новый ViewPager2 теперь рекомендуется вместо старого ViewPager, который я хочу использовать ViewPager2 и FragmentStateAdapter вместо этого в моем коде. Я нашел пример того, как объединить BottomNavigationBar и ViewPager здесь, и я хочу сделать что-то подобное. Мой код во многом похож на тот, что в примере, с той лишь разницей, что мой код находится во фрагменте, а не в действии. Вот изображение того, как выглядит мой экран FrontendFragment. Я могу переключаться между представлениями с помощью нижней панели навигации, но я также хочу иметь возможность перемещаться между представлениями. Может ли кто-нибудь помочь мне или хотя бы направить меня на правильный путь? Вот мой код:

Класс FragmentPagerAdapter:

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:

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

Заранее спасибо!


person Mohammed Abdu    schedule 17.03.2021    source источник


Ответы (2)


Поскольку я уже использую BottomNav с ViewPager2 в одном из своих приложений, я могу помочь.

Ваш код частично правильный, что означает, что ваш FragmentPagerAdapter в порядке, но не ваш FrontEndFragment.

Видите ли, FragmentPagerAdapter должен быть установлен на ViewPager2 как

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

Тогда вам вообще не нужно выполнять FragmentTransaction, вам просто нужно изменить текущую позицию элемента ViewPager2 через BottomNavigationBar как

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

Это все, вам не нужно иметь дело с Fragments, кроме FragmentPagerAdapter. Кроме того, не забудьте удалить loadFragment(new HomeFragment());, который не требуется, и функция loadFragment() не требуется.

(Необязательно). Кроме того, если вы хотите отключить действие смахивания ViewPager2 и хотите, чтобы фрагменты выбирались только на основе элемента Selected BottomNav, вы можете установить свойство setUserInputEnabled() для ViewPager2 как false.


Затем, чтобы установить элемент BottomNavigationBar как выбранный на основе движения ViewPager2, вам нужно сделать следующее:

Создать глобальную переменную

MenuItem previousMenuItem;

Затем установите элемент по умолчанию (первый) для выбора BottomNav при запуске активности как

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

Наконец, установите обратный вызов OnPageSelected() на ViewPager2, чтобы обновить выбранный пункт меню как:

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

Что вы здесь делаете, так это то, что вы устанавливаете элемент по умолчанию на previousMenuItem, а затем при переходе на другую страницу отменяете выбор previousMenuItem и выбираете новую, что означает обновление BottomNav на основе текущего элемента ViewPager. Это полный код, который вам нужен для достижения вашей цели.

person Lalit Fauzdar    schedule 17.03.2021
comment
Спасибо! Я только что реализовал ваш код, но я получаю сообщение об ошибке mViewPager2.setAdapter(new FragmentPagerAdapter());, так как мне нужно передать фрагмент в FragmentPagerAdapter. Что мне там пройти? Также мне нужно найти идентификатор viewpager2 перед настройкой адаптера? просто чтобы быть уверенным, поскольку я новичок в кодировании. В очередной раз благодарим за помощь! - person Mohammed Abdu; 17.03.2021
comment
@MohammedAbdu Да, вы должны инициализировать mViewPager2 как mViewPager2 = v.findViewById(R.id.yourViewPagerID);, а затем установить его адаптер. И в FragmentPagerAdapter вы должны передать Fragment как mViewPager2.setAdapter(new FragmentPagerAdapter(this));. Это будет работать. - person Lalit Fauzdar; 17.03.2021
comment
Большое спасибо! Я целый день боролась, пытаясь понять, как это сделать, и теперь ты спас меня! :) Это полностью работает! - person Mohammed Abdu; 17.03.2021
comment
И последнее: я пытаюсь научиться всему, поэтому мне не нужно больше бороться. Почему я передаю this как фрагмент? что означает this в этом контексте? Приносим извинения за неудобства. - person Mohammed Abdu; 17.03.2021
comment
@MohammedAbdu this всегда зависит от области, откуда вы его отправляете. Теперь вы могли заметить, что Конструктору FragmentStateAdapter требуется Фрагмент, как определено в вашем коде здесь public FragmentPagerAdapter(@NonNull Fragment fragment) {. Ему нужен FrontEndFragment, и когда вы передаете this, вы фактически передаете запущенный экземпляр FrontEndFragment от самого себя как this. Поскольку вы находитесь в переопределенной функции onCreateView(), а не в контексте другого класса, как любой обратный вызов, она работает. В противном случае вам нужно будет указать это как this@FrontEndFragment. - person Lalit Fauzdar; 18.03.2021

Если вы хотите перемещаться между представлениями, простым решением было бы сохранить все представления в родительском представлении, но установить для всех макетов для представлений, кроме исходного, значение android:visibility="gone". Не забудьте установить начальный макет вида на android:visibility="visible". Теперь при нажатии кнопок вам нужно будет реализовать onClick, чтобы они соответственно включали / выключали видимость. Например, храните представления по порядку и управляйте ими через индекс массива. Но все, что вы пытаетесь сделать, обычно не является хорошим шаблоном проектирования на мой взгляд.

Почему бы вам не загрузить еще одно действие onClick вместо того, чтобы загружать одно действие? Это вызовет проблемы со временем загрузки. Даже если виды не видны, хранить все это в одном месте - просто общая проблема.

person mindoverflow    schedule 17.03.2021