Restoring fragment state when changing fragments through bottom navigation bar

后端 未结 4 689
星月不相逢
星月不相逢 2020-12-24 09:22

I have bottom navigation bar on click of item in navigation bar i am replacing fragments. I have 3 fragments A,B,C so on click of b item B fragment is loaded and in B i am c

相关标签:
4条回答
  • 2020-12-24 09:24

    The simplest solution for that is to override "OnCreate()" method in your Fragment B and call you APIs in "OnCreate()" method instead of "OnCreateView()".

    Hope it will work for you!

    0 讨论(0)
  • 2020-12-24 09:38

    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;
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.isPagingEnabled && super.onTouchEvent(event);
    }
    
    //for samsung phones to prevent tab switching keys to show on keyboard
    @Override
    public boolean executeKeyEvent(KeyEvent event) {
        return isPagingEnabled && super.executeKeyEvent(event);
    }
    
    @Override
    public 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);
        }
    }
    
    0 讨论(0)
  • 2020-12-24 09:43

    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 link How to save fragment state in android?

    0 讨论(0)
  • 2020-12-24 09:46

    You should use ViewPager2 as it is the updated version of ViewPager.

    Step by step guide (to restore/retain an EditText's state as an example):

    Step 1:

    Add dependencies:

    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 &amp; 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 &amp; 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);
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题