Restoring fragment state when changing fragments through bottom navigation bar

家住魔仙堡 提交于 2020-05-09 18:32:41

问题


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 calling 3-4 APIs. So now if i go to C and then again come to B a new instance of B Fragment is created and again those APIs are called how can i save the fragment instance state and not call APIs again while changing fragments. This is my code.

mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            int id = item.getItemId();
            Fragment currentLoaded = fgMan.findFragmentById(R.id.container_body);
            switch (id) {
                case R.id.nearby_fragment:
                    if (!(currentLoaded instanceof SpotFeedMapFragment)) {
                        removeScroll();
                        mNearByFragment = fgMan.findFragmentByTag(NEARBY_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(NEARBY_FRAGMENT_TAG) : mNearByFragment;
                        fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
                        fgMan.beginTransaction().replace(R.id.container_body, mNearByFragment, NEARBY_FRAGMENT_TAG).commit();
                        fgMan.executePendingTransactions();
                        getSupportActionBar().setTitle(getString(R.string.nearby_fragment));
                    }
                    break;
                case R.id.route_fragment:
                    if (!(currentLoaded instanceof BusLocationsFragment)) {
                        if (!inParent) {
                            mRl.removeView(fixLayout);
                            p.addRule(RelativeLayout.BELOW, toolbar.getId());
                            scrollView.setLayoutParams(p);
                            scrollView.addView(fixLayout);
                            mRl.addView(scrollView);
                            inParent = true;
                        }
                        //mFragment = new BusLocationsFragment();
                        mBusLocFragment = fgMan.findFragmentByTag(BUS_LOC_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(BUS_LOC_FRAGMENT_TAG) : mBusLocFragment;
                        fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
                        fgMan.beginTransaction().replace(R.id.container_body, mBusLocFragment, BUS_LOC_FRAGMENT_TAG).commit();
                        fgMan.executePendingTransactions();
                        getSupportActionBar().setTitle(getString(R.string.app_name));
                    }
                    break;
                case R.id.newsfeed_activity:
                    if (!(currentLoaded instanceof NewsFeedActivity)) {
                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
                            removeScroll();
                        }
                        mNewsFeedFragment = fgMan.findFragmentByTag(NEWSFEED_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(NEWSFEED_FRAGMENT_TAG) : mNewsFeedFragment;
                        fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
                        fgMan.beginTransaction().replace(R.id.container_body, mNewsFeedFragment, NEWSFEED_FRAGMENT_TAG).commit();
                        fgMan.executePendingTransactions();
                        getSupportActionBar().setTitle(getString(R.string.news));
                    }
                    break;
            }
            return true;
        }
    });

I have already initialized fragments member variables above in onCreateof MainActivity


回答1:


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



回答2:


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?




回答3:


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!



来源:https://stackoverflow.com/questions/42781409/restoring-fragment-state-when-changing-fragments-through-bottom-navigation-bar

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!