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. When sliding to another tab and returning to original, I want the last fragment to be displayed.
For example, I have tab_a
with Fragment_1
, tab_b
with Fragment_4
and tab_c
with Fragment_7
. Now I want a button in Fragment_1
that opens me Fragment_2
, but I want to be able to swipe to the fragments of the tab_b
and tab_c
. And when I return to tab_a, Fragment_2
must be displayed.
MainActivity
|
|
ContainerFragment
|
|
|_ _ _ Tab A
| |_ _ _ Fragment 1
| |
| |_ _ _ Fragment 2
| |
| |_ _ _ Fragment 3
| |
| |_ _ _ ...
|
|_ _ _ Tab B
| |_ _ _ Fragment 4
| |
| |_ _ _ Fragment 5
| |
| |_ _ _ Fragment 6
| |
| |_ _ _ ...
|
|_ _ _ Tab C
| |_ _ _ Fragment 7
| |
| |_ _ _ Fragment 8
| |
| |_ _ _ Fragment 8
| |
| |_ _ _ ...
My current main activity.
Xml
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
Code
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private final static int[] tabIcons = {
R.drawable.ic_tab_a,
R.drawable.ic_tab_b,
R.drawable.ic_tab_c
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setupTabIcons();
}
private void setupTabIcons() {
tabLayout.getTabAt(0).setIcon(tabIcons[0]);
tabLayout.getTabAt(1).setIcon(tabIcons[1]);
tabLayout.getTabAt(2).setIcon(tabIcons[2]);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new Fragment1());
adapter.addFrag(new Fragment4());
adapter.addFrag(new Fragment7());
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
void addFrag(Fragment fragment) {
mFragmentList.add(fragment);
}
}
}
Fragments
Xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:background="@android:color/holo_blue_light"
android:id="@+id/fragment_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="myContext">
<Button
android:text="Go to next Fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:id="@+id/button_nextFrag" />
<FrameLayout
android:id="@+id/tab1_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Code
public class Fragment1 extends Fragment {
private void goNextFragment() {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.tab1_container, new Fragment2());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment.
View root = inflater.inflate(R.layout.fragment_1, container, false);
// Go to next Fragment Button.
final Button nextFrag = (Button) root.findViewById(R.id.button_nextFrag);
nextFrag .setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goNextFragment();
}
});
return root;
}
}
Edit:
I've got some solutions, but none is perfect. If I put a Framelayaout in MainActivity, Fragment_1
is correctly replaced to Fragment_2
, but it is also in tab_b
and tab_c
. If the FrameLayout is in Fragment_1
, I can swipe the other tabs correctly, but the replacement goes not good, cause Fragment_2
is opened, but Fragment_1
is also there.
Based on what you explained I Implement following:
1- In your Activity create your
Tabs
andViewPager
:2- In each Tabs create
ViewPager
(I assume you want tabs insidetabs
if you don't need tabs just removed them), Implementation of eachfragment
would be something like :Also in fragment create a Button which when Click goes to another fragment:
final result would be something like following and when If you go to Home Tabs and then goes back to Category Tab you're your
Fragment
position not changed.Two point you should remember:
mPager.setOffscreenPageLimit(3);
if you don't want to destroy your fragmentgetChildFragmentManager()
instead ofFragmentManager()
If you don't need Inner
ViewPager
And you Just want load one instance of Fragment at time here is another option:1- Do first item of previous way.
2- In your fragment xml put FrameContainer and load your fragment inside it:
Edit: This is not best approach but I think solve your issue:
1- Create a static field in your
MainActivity
like following:2- When you call onClick in your fragment, update
String
value in yourMainActivity
.first value is ViewPager position, Second value is your inner Fragment type (e.g: fragment_d);
3- In your ViewPager
You can change fragments inside page fragment. For example, TAB_A implements logic of picking fragment 1 or 2, and displaying picked fragment inside. Hierarchy looks like:
ViewPager -> TAB_A -> fragment 1 or 2 (displaying inside TAB_A).
Also you should use getChildFragmentManager() to manage fragments inside TAB_A.
You can use a
ViewPager
together with aTabLayout
.Then create a class that extends
FragmentStatePagerAdapter
to initialize theViewPager
with your initialFragment
s:The fragments containing the actual contents should be wrapped by a
HostFragment
, that uses its childFragmentManager
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 itsreplaceFragment
to navigate:The HostFragment should extend the abstract class
BackstackFragment
Wire everthing up in the
MainActivity
:This solution is certainly not perfect but gets the job done. Read more fluff about it in my blog post on the issue.