Restoring fragment state when changing fragments through bottom navigation bar
You should use a FragmentPagerAdapter to initiate the fragments so when you want to switch in between them, the state of the fragments will be saved.
CutomViewPager viewPager = (CustomViewPager) findViewById(R.id.viewpager1);ViewPagerAdapter adapter = new ViewPagerAdapter (MainActivity.this.getSupportFragmentManager());adapter.addFragment(new SpotFeedMapFragment(), "title");adapter.addFragment(new BusLocationsFragment(), "title");adapter.addFragment(new NewsFeedActivity(), "title");viewPager.setAdapter(adapter);
then in the bottom navigation selected you can set fragment by simple command
viewPager.setCurrentItem(n);
my viewpager class is as follows:
public class CustomViewPager extends ViewPager {private boolean isPagingEnabled;public CustomViewPager(Context context) { super(context); this.isPagingEnabled = true;}public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); this.isPagingEnabled = true;}@Overridepublic boolean onTouchEvent(MotionEvent event) { return this.isPagingEnabled && super.onTouchEvent(event);}//for samsung phones to prevent tab switching keys to show on keyboard@Overridepublic boolean executeKeyEvent(KeyEvent event) { return isPagingEnabled && super.executeKeyEvent(event);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) { return this.isPagingEnabled && super.onInterceptTouchEvent(event);}public void setPagingEnabled(boolean enabled) { this.isPagingEnabled = enabled;}}
in the xml instead of a empty layout for fragemnt u need:
<com.package.util.CustomViewPager android:id="@+id/viewpager1" android:layout_width="match_parent" android:layout_height="match_parent" />
Code for custom FragmentPagerAdapter:
private class ViewPagerAdapter extends FragmentPagerAdapter { private final SparseArray<WeakReference<Fragment>> instantiatedFragments = new SparseArray<>(); private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = 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 addFragment(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public Object instantiateItem(ViewGroup container, int position) { final Fragment fragment = (Fragment) super.instantiateItem(container, position); instantiatedFragments.put(position, new WeakReference<>(fragment)); return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { instantiatedFragments.remove(position); super.destroyItem(container, position, object); } @Nullable Fragment getFragment(final int position) { final WeakReference<Fragment> wr = instantiatedFragments.get(position); if (wr != null) { return wr.get(); } else { return null; } } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); }}
I used bottom navigation bar
and I did it by customizing viewpager and I disable the swipe navigation. Each time user clicks bottom item, set relevant fragment in viewpager. Viewpager control state of fragment, so no need control state.
Custom ViewPager
public class BottomNavigationViewPager extends ViewPager { private boolean enabled; public BottomNavigationViewPager(Context context, AttributeSet attrs) { super(context, attrs); this.enabled = false; } @Override public boolean onTouchEvent(MotionEvent event) { if (this.enabled) { return super.onTouchEvent(event); } return false; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (this.enabled) { return super.onInterceptTouchEvent(event); } return false; } /** * Enable or disable the swipe navigation * @param enabled */ public void setPagingEnabled(boolean enabled) { this.enabled = enabled; }}
If you still want to control state of fragment, you can see my answer in this linkHow to save fragment state in android?
To restore/retain a fragment's state you should use ViewPager2 as it is the updated version of ViewPager.
You will get the code on my GitHub repository with three menu items in the Bottom Navigation Bar with more functionality. I am also providing a simple description here with two menu items in the Bottom Navigation Bar.
Step by step guide (to restore/retain an EditText's state as an example):
Step 1:
Add dependencies in your build.gradle (app module) file:
dependencies { def nav_version = "2.3.0" implementation "androidx.navigation:navigation-fragment:$nav_version" implementation "androidx.navigation:navigation-ui:$nav_version" implementation 'androidx.viewpager2:viewpager2:1.0.0'}
Step 2:
Add menu_bottom_navigation.xml to res/menu: (You may also add icons to menu items)
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_first" android:checked="true" android:title="First" app:showAsAction="always" /> <item android:id="@+id/menu_second" android:checked="false" android:title="Second" app:showAsAction="always" /></menu>
Step 3:
Add activity_main.xml to res/layout: (adding menu to BottomNavigationView and placing ViewPager2)
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activityRoot" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom" android:orientation="vertical" android:animateLayoutChanges="true" tools:context=".MainActivity"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewpager2" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/bottom_navigation" android:layout_alignParentTop="true" android:layout_weight="1" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_gravity="bottom" android:fitsSystemWindows="true" app:itemIconSize="20dp" android:background="#A8DD44" app:menu="@menu/menu_bottom_navigation" /></LinearLayout>
Step 4:
Add fragment_first.xml to res/layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="20dp" tools:context="com.example.rough.Fragment.FirstFragment"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="First Fragment" android:layout_centerInParent="true" android:textSize="30sp" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Write something & it will stay" android:ems="13"/></LinearLayout>
Step 5:
Add fragment_second.xml to res/layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="20dp" android:orientation="vertical" tools:context="com.example.rough.Fragment.SecondFragment"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Second Fragment" android:layout_centerInParent="true" android:textSize="30sp" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Write something & it will stay" android:ems="13"/></LinearLayout>
Step 6:
ViewPagerAdapter.java:
public class ViewPagerAdapter extends FragmentStateAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); public ViewPagerAdapter(@NonNull FragmentManager fragmentManager, Lifecycle b ) { super(fragmentManager,b); } public void addFragment(Fragment fragment) { mFragmentList.add(fragment); } @NonNull @Override public Fragment createFragment(int position) { return mFragmentList.get(position); } @Override public int getItemCount() { return mFragmentList.size(); }}
Step 7:
FirstFragment.java:
public class FirstFragment extends Fragment { public FirstFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_first, container, false); }}
Step 8:
SecondFragment.java:
public class SecondFragment extends Fragment { public SecondFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_second, container, false); }}
Step 9:
MainActivity.java:
public class MainActivity extends AppCompatActivity { BottomNavigationView bottomNavigationView; private ViewPager2 viewPager2; FirstFragment firstFragment; SecondFragment secondFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager2 = findViewById(R.id.viewpager2); bottomNavigationView = findViewById(R.id.bottom_navigation); bottomNavigationView.setOnNavigationItemSelectedListener( new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.menu_first: viewPager2.setCurrentItem(0,false); break; case R.id.menu_second: viewPager2.setCurrentItem(1,false); break; } return false; } }); viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); switch (position) { case 0: bottomNavigationView.getMenu().findItem(R.id.menu_first).setChecked(true); break; case 1: bottomNavigationView.getMenu().findItem(R.id.menu_second).setChecked(true); break; } } }); setupViewPager(viewPager2); } private void setupViewPager(ViewPager2 viewPager) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle()); firstFragment =new FirstFragment(); secondFragment =new SecondFragment(); adapter.addFragment(firstFragment); adapter.addFragment(secondFragment); viewPager.setAdapter(adapter); }}