Restoring fragment state when changing fragments through bottom navigation bar Restoring fragment state when changing fragments through bottom navigation bar android android

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