Replace Fragment inside a ViewPager

后端 未结 18 1349
别那么骄傲
别那么骄傲 2020-11-22 00:26

I\'m trying to use Fragment with a ViewPager using the FragmentPagerAdapter. What I\'m looking for to achieve is to replace a fragment, positioned

相关标签:
18条回答
  • 2020-11-22 01:10

    Based on @wize 's answer, which I found helpful and elegant, I could achieve what I wanted partially, cause I wanted the cability to go back to the first Fragment once replaced. I achieved it bit modifying a bit his code.

    This would be the FragmentPagerAdapter:

    public static class MyAdapter extends FragmentPagerAdapter {
        private final class CalendarPageListener implements
                CalendarPageFragmentListener {
            public void onSwitchToNextFragment() {
                mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                        .commit();
                if (mFragmentAtPos0 instanceof FirstFragment){
                    mFragmentAtPos0 = NextFragment.newInstance(listener);
                }else{ // Instance of NextFragment
                    mFragmentAtPos0 = FirstFragment.newInstance(listener);
                }
                notifyDataSetChanged();
            }
        }
    
        CalendarPageListener listener = new CalendarPageListener();;
        private Fragment mFragmentAtPos0;
        private FragmentManager mFragmentManager;
    
        public MyAdapter(FragmentManager fm) {
            super(fm);
            mFragmentManager = fm;
        }
    
        @Override
        public int getCount() {
            return NUM_ITEMS;
        }
    
        @Override
        public int getItemPosition(Object object) {
            if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
                return POSITION_NONE;
            if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
                return POSITION_NONE;
            return POSITION_UNCHANGED;
        }
    
        @Override
        public Fragment getItem(int position) {
            if (position == 0)
                return Portada.newInstance();
            if (position == 1) { // Position where you want to replace fragments
                if (mFragmentAtPos0 == null) {
                    mFragmentAtPos0 = FirstFragment.newInstance(listener);
                }
                return mFragmentAtPos0;
            }
            if (position == 2)
                return Clasificacion.newInstance();
            if (position == 3)
                return Informacion.newInstance();
    
            return null;
        }
    }
    
    public interface CalendarPageFragmentListener {
        void onSwitchToNextFragment();
    }
    

    To perfom the replacement, simply define a static field, of the type CalendarPageFragmentListener and initialized through the newInstance methods of the corresponding fragments and call FirstFragment.pageListener.onSwitchToNextFragment() or NextFragment.pageListener.onSwitchToNextFragment() respictevely.

    0 讨论(0)
  • 2020-11-22 01:11

    Here's my relatively simple solution to this problem. The keys to this solution are to use FragmentStatePagerAdapter instead of FragmentPagerAdapter as the former will remove unused fragments for you while the later still retains their instances. The second is the use of POSITION_NONE in getItem(). I've used a simple List to keep track of my fragments. My requirement was to replace the entire list of fragments at once with a new list, but the below could be easily modified to replace individual fragments:

    public class MyFragmentAdapter extends FragmentStatePagerAdapter {
        private List<Fragment> fragmentList = new ArrayList<Fragment>();
        private List<String> tabTitleList = new ArrayList<String>();
    
        public MyFragmentAdapter(FragmentManager fm) {
            super(fm);
        }
    
        public void addFragments(List<Fragment> fragments, List<String> titles) {
            fragmentList.clear();
            tabTitleList.clear();
            fragmentList.addAll(fragments);
            tabTitleList.addAll(titles);
            notifyDataSetChanged();
        }
    
        @Override
        public int getItemPosition(Object object) {
            if (fragmentList.contains(object)) {
                return POSITION_UNCHANGED;
            }
            return POSITION_NONE;
        }
    
        @Override
        public Fragment getItem(int item) {
            if (item >= fragmentList.size()) {
                return null;
            }
            return fragmentList.get(item);
        }
    
        @Override
        public int getCount() {
            return fragmentList.size();
        }
    
        @Override
        public CharSequence getPageTitle(int position) {
            return tabTitleList.get(position);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:11

    I doing something to similar to wize but in my answer yo can change between the two fragments whenever you want. And with the wize answer I have some problems when changing the orientation of the screen an things like that. This is the PagerAdapter looks like:

        public class MyAdapter extends FragmentPagerAdapter
    {
        static final int NUM_ITEMS = 2;
        private final FragmentManager mFragmentManager;
        private Fragment mFragmentAtPos0;
         private Map<Integer, String> mFragmentTags;
         private boolean isNextFragment=false;
    
        public MyAdapter(FragmentManager fm)
        {
            super(fm);
            mFragmentManager = fm;
             mFragmentTags = new HashMap<Integer, String>();
        }
    
        @Override
        public Fragment getItem(int position)
        {
            if (position == 0)
            {
    
    
                if (isPager) {
                    mFragmentAtPos0 = new FirstPageFragment();
                } else {
                    mFragmentAtPos0 = new NextFragment();
                }
                return mFragmentAtPos0;
            }
            else
                return SecondPageFragment.newInstance();
        }
    
        @Override
        public int getCount()
        {
            return NUM_ITEMS;
        }
    
    
     @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object obj = super.instantiateItem(container, position);
            if (obj instanceof Fragment) {
                // record the fragment tag here.
                Fragment f = (Fragment) obj;
                String tag = f.getTag();
                mFragmentTags.put(position, tag);
            }
            return obj;
        }
    
    
        public void onChange(boolean isNextFragment) {
    
            if (mFragmentAtPos0 == null)
                mFragmentAtPos0 = getFragment(0);
            if (mFragmentAtPos0 != null)
                mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
    
    
            if (!isNextFragment) {
                mFragmentAtFlashcards = new FirstPageFragment();
            } else {
                mFragmentAtFlashcards = new NextFragment();
            }
    
            notifyDataSetChanged();
    
    
        }
    
    
        @Override
        public int getItemPosition(Object object)
        {
            if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
                return POSITION_NONE;
             if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
                return POSITION_NONE;
            return POSITION_UNCHANGED;
        }
    
    
        public Fragment getFragment(int position) {
            String tag = mFragmentTags.get(position);
            if (tag == null)
                return null;
            return mFragmentManager.findFragmentByTag(tag);
        }
    }
    

    The listener I implemented in the adapter container activity to put it to the fragment when attaching it, this is the activity:

        public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {
    
    //...
    
      @Override
        public void onChange(boolean isNextFragment) {
            if (pagerAdapter != null)
                pagerAdapter.onChange(isNextFragment);
    
    
        }
    
    //...
    }
    

    Then in the fragment putting the listener when attach an calling it:

    public class FirstPageFragment extends Fragment{
    
    
    private ChangeFragmentListener changeFragmentListener;
    
    
    //...
     @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            changeFragmentListener = ((PagerContainerActivity) activity);
        }
    
        @Override
        public void onDetach() {
            super.onDetach();
            changeFragmentListener = null;
        }
    //...
    //in the on click to change the fragment
    changeFragmentListener.onChange(true);
    //...
    }
    

    And finally the listener:

    public interface changeFragmentListener {
    
        void onChange(boolean isNextFragment);
    
    }
    
    0 讨论(0)
  • 2020-11-22 01:11

    I followed the answers by @wize and @mdelolmo and I got the solution. Thanks Tons. But, I tuned these solutions a little bit to improve the memory consumption.

    Problems I observed:

    They save the instance of Fragment which is replaced. In my case, it is a Fragment which holds MapView and I thought its costly. So, I am maintaining the FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED) instead of Fragment itself.

    Here is my implementation.

      public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
    
        private SwitchFragListener mSwitchFragListener;
        private Switch mToggle;
        private int pagerAdapterPosChanged = POSITION_UNCHANGED;
        private static final int TOGGLE_ENABLE_POS = 2;
    
    
        public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
            super(fm);
            mToggle = toggle;
    
            mSwitchFragListener = new SwitchFragListener();
            mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    mSwitchFragListener.onSwitchToNextFragment();
                }
            });
        }
    
        @Override
        public Fragment getItem(int i) {
            switch (i)
            {
                case TOGGLE_ENABLE_POS:
                    if(mToggle.isChecked())
                    {
                        return TabReplaceFragment.getInstance();
                    }else
                    {
                        return DemoTab2Fragment.getInstance(i);
                    }
    
                default:
                    return DemoTabFragment.getInstance(i);
            }
        }
    
        @Override
        public int getCount() {
            return 5;
        }
    
        @Override
        public CharSequence getPageTitle(int position) {
            return "Tab " + (position + 1);
        }
    
        @Override
        public int getItemPosition(Object object) {
    
            //  This check make sures getItem() is called only for the required Fragment
            if (object instanceof TabReplaceFragment
                    ||  object instanceof DemoTab2Fragment)
                return pagerAdapterPosChanged;
    
            return POSITION_UNCHANGED;
        }
    
        /**
         * Switch fragments Interface implementation
         */
        private final class SwitchFragListener implements
                SwitchFragInterface {
    
            SwitchFragListener() {}
    
            public void onSwitchToNextFragment() {
    
                pagerAdapterPosChanged = POSITION_NONE;
                notifyDataSetChanged();
            }
        }
    
        /**
         * Interface to switch frags
         */
        private interface SwitchFragInterface{
            void onSwitchToNextFragment();
        }
    }
    

    Demo link here.. https://youtu.be/l_62uhKkLyM

    For demo purpose, used 2 fragments TabReplaceFragment and DemoTab2Fragment at position two. In all the other cases I'm using DemoTabFragment instances.

    Explanation:

    I'm passing Switch from Activity to the DemoCollectionPagerAdapter. Based on the state of this switch we will display correct fragment. When the switch check is changed, I'm calling the SwitchFragListener's onSwitchToNextFragment method, where I'm changing the value of pagerAdapterPosChanged variable to POSITION_NONE. Check out more about POSITION_NONE. This will invalidate the getItem and I have logics to instantiate the right fragment over there. Sorry, if the explanation is a bit messy.

    Once again big thanks to @wize and @mdelolmo for the original idea.

    Hope this is helpful. :)

    Let me know if this implementation has any flaws. That will be greatly helpful for my project.

    0 讨论(0)
  • 2020-11-22 01:16

    after research i found solution with short code. first of all create a public instance on fragment and just remove your fragment on onSaveInstanceState if fragment not recreating on orientation change.

     @Override
    public void onSaveInstanceState(Bundle outState) {
        if (null != mCalFragment) {
            FragmentTransaction bt = getChildFragmentManager().beginTransaction();
            bt.remove(mFragment);
            bt.commit();
        }
        super.onSaveInstanceState(outState);
    }
    
    0 讨论(0)
  • 2020-11-22 01:19

    This is my way to achieve that.

    First of all add Root_fragment inside viewPager tab in which you want to implement button click fragment event. Example;

    @Override
    public Fragment getItem(int position) {
      if(position==0)
          return RootTabFragment.newInstance();
      else
          return SecondPagerFragment.newInstance();
    }
    

    First of all, RootTabFragment should be include FragmentLayout for fragment change.

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:id="@+id/root_frame"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
    </FrameLayout>
    

    Then, inside RootTabFragment onCreateView, implement fragmentChange for your FirstPagerFragment

    getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();
    

    After that, implement onClick event for your button inside FirstPagerFragment and make fragment change like that again.

    getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();
    

    Hope this will help you guy.

    0 讨论(0)
提交回复
热议问题