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
You can use a ViewPager
together with a TabLayout
.
Then create a class that extends FragmentStatePagerAdapter
to initialize the ViewPager
with your initial Fragment
s:
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.