Separate Back Navigation for a Tabbed View Pager in Android

前端 未结 3 1014
傲寒
傲寒 2021-02-09 02:48

What I want.

In a tab sliding menu context, I want to replace a fragment to another inside a tab, and maintaining the tab menu, and also the current tab

3条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-02-09 03:23

    You can use a ViewPager together with a TabLayout.

    
    
    
    
    
    
    

    Then create a class that extends FragmentStatePagerAdapter to initialize the ViewPager with your initial Fragments:

    public class CustomPagerAdapter extends FragmentStatePagerAdapter {
        private final List tabTitles = new ArrayList() {{
            add("Fragment 1");
            add("Fragment 4");
            add("Fragment 7");
        }};
    
        private List tabs = new ArrayList<>();
    
        public CustomPagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
    
            initializeTabs();
        }
    
        private void initializeTabs() {
            tabs.add(HostFragment.newInstance(new Fragment1()));
            tabs.add(HostFragment.newInstance(new Fragment4()));
            tabs.add(HostFragment.newInstance(new Fragment7()));
        }
    
        @Override
        public Fragment getItem(int position) {
            return tabs.get(position);
        }
    
        @Override
        public int getCount() {
            return tabs.size();
        }
    
        @Override
        public CharSequence getPageTitle(int position) {
            return tabTitles.get(position);
        }
    }
    

    The fragments containing the actual contents should be wrapped by a HostFragment, that uses its child FragmentManager to replace the current fragment with a new one if you want to navigate, or to pop the last fragment from the fragment stack. Call its replaceFragment to navigate:

    public class HostFragment extends BackStackFragment {
        private Fragment fragment;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            super.onCreateView(inflater, container, savedInstanceState);
            View view = inflater.inflate(R.layout.host_fragment, container, false);
            if (fragment != null) {
                replaceFragment(fragment, false);
            }
            return view;
        }
    
        public void replaceFragment(Fragment fragment, boolean addToBackstack) {
            if (addToBackstack) {
                getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
            } else {
                getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
            }
        }
    
        public static HostFragment newInstance(Fragment fragment) {
            HostFragment hostFragment = new HostFragment();
            hostFragment.fragment = fragment;
            return hostFragment;
        }
    }
    

    The HostFragment should extend the abstract class BackstackFragment

    public abstract class BackStackFragment extends Fragment {
        public static boolean handleBackPressed(FragmentManager fm)
        {
            if(fm.getFragments() != null){
                for(Fragment frag : fm.getFragments()){
                    if(frag != null && frag.isVisible() && frag instanceof BackStackFragment){
                        if(((BackStackFragment)frag).onBackPressed()){
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    
        protected boolean onBackPressed()
        {
            FragmentManager fm = getChildFragmentManager();
            if(handleBackPressed(fm)){
                return true;
            } else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
                fm.popBackStack();
                return true;
            }
            return false;
        }
    }
    

    Wire everthing up in the MainActivity:

    public class MainActivity extends AppCompatActivity {
        private CustomPagerAdapter customPagerAdapter;
        private ViewPager viewPager;
        private TabLayout tabLayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            viewPager = (ViewPager) findViewById(R.id.viewpager);
            tabLayout = (TabLayout) findViewById(R.id.tablayout);
            customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager());
    
            // 2 is enough for us; increase if you have more tabs!
            viewPager.setOffscreenPageLimit(2);
            viewPager.setAdapter(customPagerAdapter);
            tabLayout.setupWithViewPager(viewPager);
        }
    
        @Override
        public void onBackPressed()
        {
            if(!BackStackFragment.handleBackPressed(getSupportFragmentManager())){
                super.onBackPressed();
            }
        }
    
        public void openNextFragment() {
            HostFragment hostFragment = (HostFragment) customPagerAdapter.getItem(viewPager.getCurrentItem());
    
            // your logic to change the fragments...
        }
    }
    

    This solution is certainly not perfect but gets the job done. Read more fluff about it in my blog post on the issue.

提交回复
热议问题