首页 文章

替换ViewPager中的Fragment

提问于
浏览
246

我正在尝试使用 ViewPager 使用 ViewPager 使用Fragment . 我想要实现的是将位于 ViewPager 的第一页上的片段替换为另一片段 .

寻呼机由两页组成 . 第一个是 FirstPagerFragment ,第二个是 SecondPagerFragment . 单击第一页的按钮 . 我想用NextFragment替换 FirstPagerFragment .

我的代码如下 .

public class FragmentPagerActivity extends FragmentActivity {

    static final int NUM_ITEMS = 2;

    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

    }


    /**
     * Pager Adapter
     */
    public static class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {

            if(position == 0) {
                return FirstPageFragment.newInstance();
            } else {
                return SecondPageFragment.newInstance();
            }

        }
    }


    /**
     * Second Page FRAGMENT
     */
    public static class SecondPageFragment extends Fragment {

        public static SecondPageFragment newInstance() {
            SecondPageFragment f = new SecondPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.second, container, false);

        }
    }

    /**
     * FIRST PAGE FRAGMENT
     */
    public static class FirstPageFragment extends Fragment {

        Button button;

        public static FirstPageFragment newInstance() {
            FirstPageFragment f = new FirstPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            View root = inflater.inflate(R.layout.first, container, false);
            button = (Button) root.findViewById(R.id.button);
            button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    FragmentTransaction trans = getFragmentManager().beginTransaction();
                                    trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
                    trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                    trans.addToBackStack(null);
                    trans.commit();

                }

            });

            return root;
        }

        /**
     * Next Page FRAGMENT in the First Page
     */
    public static class NextFragment extends Fragment {

        public static NextFragment newInstance() {
            NextFragment f = new NextFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.next, container, false);

        }
    }
}

...and here the xml files

fragment_pager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:padding="4dip"
        android:gravity="center_horizontal"
        android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">
    </android.support.v4.view.ViewPager>

</LinearLayout>

first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/first_fragment_root_id"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button android:id="@+id/button"
     android:layout_width="wrap_content" android:layout_height="wrap_content"
     android:text="to next"/>

</LinearLayout>

现在问题是......我应该使用哪个ID

trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());

如果我使用 R.id.first_fragment_root_id ,则替换有效,但层次结构查看器显示奇怪的行为,如下所示 .

一开始的情况是

更换后的情况是

正如你所看到的那样,有一些问题,我希望在更换片段之后找到与第一张图片中显示的状态相同的状态 .

18 回答

  • 36

    我也提出了一个与 Stacks 合作的解决方案 . 这是一个更加 modular 的方法,所以你不必在 FragmentPagerAdapter 中指定每个片段和细节片段 . 它直接来自谷歌演示应用程序's build on top of the Example from ActionbarSherlock which derives if I' .

    /**
     * This is a helper class that implements the management of tabs and all
     * details of connecting a ViewPager with associated TabHost.  It relies on a
     * trick.  Normally a tab host has a simple API for supplying a View or
     * Intent that each tab will show.  This is not sufficient for switching
     * between pages.  So instead we make the content part of the tab host
     * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
     * view to show as the tab content.  It listens to changes in tabs, and takes
     * care of switch to the correct paged in the ViewPager whenever the selected
     * tab changes.
     * 
     * Changed to support more Layers of fragments on each Tab.
     * by sebnapi (2012)
     * 
     */
    public class TabsAdapter extends FragmentPagerAdapter
            implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
        private final Context mContext;
        private final TabHost mTabHost;
        private final ViewPager mViewPager;
    
        private ArrayList<String> mTabTags = new ArrayList<String>();
        private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();
    
        static final class TabInfo {
            public final String tag;
            public final Class<?> clss;
            public Bundle args;
    
            TabInfo(String _tag, Class<?> _class, Bundle _args) {
                tag = _tag;
                clss = _class;
                args = _args;
            }
        }
    
        static class DummyTabFactory implements TabHost.TabContentFactory {
            private final Context mContext;
    
            public DummyTabFactory(Context context) {
                mContext = context;
            }
    
            @Override
            public View createTabContent(String tag) {
                View v = new View(mContext);
                v.setMinimumWidth(0);
                v.setMinimumHeight(0);
                return v;
            }
        }
    
        public interface SaveStateBundle{
            public Bundle onRemoveFragment(Bundle outState);
        }
    
        public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            mContext = activity;
            mTabHost = tabHost;
            mViewPager = pager;
            mTabHost.setOnTabChangedListener(this);
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }
    
        /**
         * Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
         * addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
         * The Stack will hold always the default Fragment u add here.
         * 
         * DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
         * beahvior.
         * 
         * @param tabSpec
         * @param clss
         * @param args
         */
        public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
            Stack<TabInfo> tabStack = new Stack<TabInfo>();
    
            tabSpec.setContent(new DummyTabFactory(mContext));
            mTabHost.addTab(tabSpec);
            String tag = tabSpec.getTag();
            TabInfo info = new TabInfo(tag, clss, args);
    
            mTabTags.add(tag);                  // to know the position of the tab tag 
            tabStack.add(info);
            mTabStackMap.put(tag, tabStack);
            notifyDataSetChanged();
        }
    
        /**
         * Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
         * it will be instantiated by this object. Proivde _args for your Fragment.
         * 
         * @param fm
         * @param _tag
         * @param _class
         * @param _args
         */
        public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
            TabInfo info = new TabInfo(_tag, _class, _args);
            Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
            Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
            if(frag instanceof SaveStateBundle){
                Bundle b = new Bundle();
                ((SaveStateBundle) frag).onRemoveFragment(b);
                tabStack.peek().args = b;
            }
            tabStack.add(info);
            FragmentTransaction ft = fm.beginTransaction();
            ft.remove(frag).commit();
            notifyDataSetChanged();
        }
    
        /**
         * Will pop the Fragment added to the Tab with the Tag _tag
         * 
         * @param fm
         * @param _tag
         * @return
         */
        public boolean popFragment(FragmentManager fm, String _tag){
            Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
            if(tabStack.size()>1){
                tabStack.pop();
                Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
                FragmentTransaction ft = fm.beginTransaction();
                ft.remove(frag).commit();
                notifyDataSetChanged();
                return true;
            }
            return false;
        }
    
        public boolean back(FragmentManager fm) {
            int position = mViewPager.getCurrentItem();
            return popFragment(fm, mTabTags.get(position));
        }
    
        @Override
        public int getCount() {
            return mTabStackMap.size();
        }
    
        @Override
        public int getItemPosition(Object object) {
            ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
            for(Stack<TabInfo> tabStack: mTabStackMap.values()){
                positionNoneHack.add(tabStack.peek().clss);
            }   // if the object class lies on top of our stacks, we return default
            if(positionNoneHack.contains(object.getClass())){
                return POSITION_UNCHANGED;
            }
            return POSITION_NONE;
        }
    
        @Override
        public Fragment getItem(int position) {
            Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
            TabInfo info = tabStack.peek();
            return Fragment.instantiate(mContext, info.clss.getName(), info.args);
        }
    
        @Override
        public void onTabChanged(String tabId) {
            int position = mTabHost.getCurrentTab();
            mViewPager.setCurrentItem(position);
        }
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }
    
        @Override
        public void onPageSelected(int position) {
            // Unfortunately when TabHost changes the current tab, it kindly
            // also takes care of putting focus on it when not in touch mode.
            // The jerk.
            // This hack tries to prevent this from pulling focus out of our
            // ViewPager.
            TabWidget widget = mTabHost.getTabWidget();
            int oldFocusability = widget.getDescendantFocusability();
            widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
            mTabHost.setCurrentTab(position);
            widget.setDescendantFocusability(oldFocusability);
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
        }
    
    }
    

    在MainActivity中为 back button 功能添加此项:

    @Override
    public void onBackPressed() {
      if (!mTabsAdapter.back(getSupportFragmentManager())) {
        super.onBackPressed();
      }
    }
    

    当你被删除时,如果你喜欢 save the Fragment State . 让你的Fragment实现接口 SaveStateBundle 在函数中返回一个带有保存状态的bundle . 通过 this.getArguments() 实例化后获取包 .

    你可以 instantiate 这样一个标签:

    mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
                    FirstFragmentActivity.FirstFragmentFragment.class, null);
    

    如果你想在标签栈顶部添加一个片段,那么工作方式类似 . Important :我认为,如果你想在两个Tabs的顶部有2个相同类的实例,它将无法工作 . 我一起快速完成了这个解决方案,所以我只能在不提供任何经验的情况下分享它 .

  • 2

    还有另一种解决方案不需要修改 ViewPagerFragmentStatePagerAdapter 的源代码,它适用于作者使用的 FragmentPagerAdapter 基类 .

    我想首先回答作者关于他应该使用哪个ID的问题;它是容器的ID,即视图寻呼机本身的ID . 但是,正如您可能已经注意到的那样,在代码中使用该ID不会导致任何事情发生 . 我会解释原因:

    首先,要使 ViewPager 重新填充页面,您需要调用驻留在适配器基类中的 notifyDataSetChanged() .

    其次, ViewPager 使用 getItemPosition() 抽象方法来检查应销毁哪些页面以及应保留哪些页面 . 此函数的默认实现始终返回 POSITION_UNCHANGED ,这会导致 ViewPager 保留所有当前页面,因此不会附加新页面 . 因此,要使片段替换工作,需要在适配器中重写 getItemPosition() ,并且在使用旧的隐藏片段作为参数调用时必须返回 POSITION_NONE .

    这也意味着您的适配器始终需要知道应该在位置0, FirstPageFragmentNextFragment 中显示哪个片段 . 执行此操作的一种方法是在创建 FirstPageFragment 时提供侦听器,在切换片段时将调用该侦听器 . 我认为这是一件好事,让你的片段适配器处理所有片段开关并调用 ViewPagerFragmentManager .

    第三, FragmentPagerAdapter 通过从位置派生的名称来缓存使用过的片段,因此如果在位置0处有片段,即使该类是新的,也不会被替换 . 有两种解决方案,但最简单的方法是使用 FragmentTransactionremove() 函数,它也将删除其标记 .

    这是很多文本,这里的代码应该适用于您的情况:

    public class MyAdapter extends FragmentPagerAdapter
    {
        static final int NUM_ITEMS = 2;
        private final FragmentManager mFragmentManager;
        private Fragment mFragmentAtPos0;
    
        public MyAdapter(FragmentManager fm)
        {
            super(fm);
            mFragmentManager = fm;
        }
    
        @Override
        public Fragment getItem(int position)
        {
            if (position == 0)
            {
                if (mFragmentAtPos0 == null)
                {
                    mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
                    {
                        public void onSwitchToNextFragment()
                        {
                            mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
                            mFragmentAtPos0 = NextFragment.newInstance();
                            notifyDataSetChanged();
                        }
                    });
                }
                return mFragmentAtPos0;
            }
            else
                return SecondPageFragment.newInstance();
        }
    
        @Override
        public int getCount()
        {
            return NUM_ITEMS;
        }
    
        @Override
        public int getItemPosition(Object object)
        {
            if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
                return POSITION_NONE;
            return POSITION_UNCHANGED;
        }
    }
    
    public interface FirstPageFragmentListener
    {
        void onSwitchToNextFragment();
    }
    

    希望这有助于任何人!

  • 2

    截至2012年11月13日,在ViewPager中重新调整片段似乎变得更加容易 . Google发布了支持嵌套片段的Android 4.2,并且在新的Android Support Library v11中也支持它,所以这将一直回到1.6

    除了使用getChildFragmentManager之外,它与替换片段的常规方法非常相似 . 当用户单击后退按钮时,除了nested fragment backstack isn't popped之外似乎有效 . 根据该链接问题中的解决方案,您需要在片段的子管理器上手动调用popBackStackImmediate() . 所以你需要覆盖ViewPager活动的onBackPressed(),你将获得ViewPager的当前片段并在其上调用getChildFragmentManager() . popBackStackImmediate() .

    获取当前正在显示的片段有点hacky,我使用了dirty "android:switcher:VIEWPAGER_ID:INDEX" solution但您也可以自己跟踪ViewPager的所有片段,如第二个解决方案on this page中所述 .

    所以这是我的ViewPager的代码,其中包含4个ListViews,当用户点击一行时,ViewPager中显示详细视图,并且后退按钮正常工作 . 为了简洁起见,我尝试仅包含相关代码,如果您希望将完整的应用程序上传到GitHub,请留言 .

    HomeActivity.java

    public class HomeActivity extends SherlockFragmentActivity {
    FragmentAdapter mAdapter;
    ViewPager mPager;
    TabPageIndicator mIndicator;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mAdapter = new FragmentAdapter(getSupportFragmentManager());
        mPager = (ViewPager)findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);
        mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
        mIndicator.setViewPager(mPager);
    }
    
    // This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
    @Override
    public void onBackPressed() {
        Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
        if (fragment != null) // could be null if not instantiated yet
        {
            if (fragment.getView() != null) {
                // Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
                if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
                    finish();
                }
            }
        }
    }
    
    class FragmentAdapter extends FragmentPagerAdapter {        
        public FragmentAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return ListProductsFragment.newInstance();
            case 1:
                return ListActiveSubstancesFragment.newInstance();
            case 2:
                return ListProductFunctionsFragment.newInstance();
            case 3:
                return ListCropsFragment.newInstance();
            default:
                return null;
            }
        }
    
        @Override
        public int getCount() {
            return 4;
        }
    
     }
    }
    

    ListProductsFragment.java

    public class ListProductsFragment extends SherlockFragment {
    private ListView list;
    
    public static ListProductsFragment newInstance() {
        ListProductsFragment f = new ListProductsFragment();
        return f;
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View V = inflater.inflate(R.layout.list, container, false);
        list = (ListView)V.findViewById(android.R.id.list);
        list.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
              // This is important bit
              Fragment productDetailFragment = FragmentProductDetail.newInstance();
              FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
              transaction.addToBackStack(null);
              transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
            }
          });       
        return V;
    }
    }
    
  • 2

    这是我相对简单的解决方案问题 . 此解决方案的关键是使用 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter ,因为前者将为您删除未使用的片段,而后者仍保留其实例 . 第二个是在getItem()中使用 POSITION_NONE . 我用一个简单的List来跟踪我的碎片 . 我的要求是使用新列表立即替换整个片段列表,但可以轻松修改以下内容以替换单个片段:

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

    我找到了简单的解决方案,即使您想在中间添加新片段或替换当前片段,也能正常工作 . 在我的解决方案中,您应该覆盖 getItemId() ,它应该返回每个片段的唯一ID . 默认情况下不定位 .

    有它:

    public class DynamicPagerAdapter extends FragmentPagerAdapter {
    
    private ArrayList<Page> mPages = new ArrayList<Page>();
    private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
    
    public DynamicPagerAdapter(FragmentManager fm) {
        super(fm);
    }
    
    public void replacePage(int position, Page page) {
        mPages.set(position, page);
        notifyDataSetChanged();
    }
    
    public void setPages(ArrayList<Page> pages) {
        mPages = pages;
        notifyDataSetChanged();
    }
    
    @Override
    public Fragment getItem(int position) {
        if (mPages.get(position).mPageType == PageType.FIRST) {
            return FirstFragment.newInstance(mPages.get(position));
        } else {
            return SecondFragment.newInstance(mPages.get(position));
        }
    }
    
    @Override
    public int getCount() {
        return mPages.size();
    }
    
    @Override
    public long getItemId(int position) {
        // return unique id
        return mPages.get(position).getId();
    }
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        mFragments.set(position, fragment);
        return fragment;
    }
    
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        mFragments.set(position, null);
    }
    
    @Override
    public int getItemPosition(Object object) {
        PagerFragment pagerFragment = (PagerFragment) object;
        Page page = pagerFragment.getPage();
        int position = mFragments.indexOf(pagerFragment);
        if (page.equals(mPages.get(position))) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }
    }
    

    注意:在此示例中, FirstFragmentSecondFragment 扩展了抽象类PageFragment,其方法为 getPage() .

  • 1

    onCreateView 方法中, container 实际上是 ViewPager 实例 .

    所以,只是打电话

    ViewPager vpViewPager = (ViewPager) container;
    vpViewPager.setCurrentItem(1);
    

    将更改 ViewPager 中的当前片段 .

  • 21

    我已经为索引2和3创建了一个包含3个元素和2个子元素的ViewPager,这就是我想要做的事情 .

    enter image description here

    我已经在之前的问题和StackOverFlow的答案的帮助下实现了这一点,这里是链接 .

    ViewPagerChildFragments

  • 1

    经过研究,我找到了短代码的解决方案 . 首先在片段上创建一个公共实例,如果片段没有在方向更改时重新创建,则只需删除onSaveInstanceState上的片段 .

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (null != mCalFragment) {
            FragmentTransaction bt = getChildFragmentManager().beginTransaction();
            bt.remove(mFragment);
            bt.commit();
        }
        super.onSaveInstanceState(outState);
    }
    
  • 0

    我做了类似于wize的事情,但在我的回答中你可以随时在两个片段之间进行更改 . 随着wize的回答,我在改变屏幕的方向时遇到了一些问题 . 这是PagerAdapter的样子:

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

    我在适配器容器活动中实现的侦听器在附加它时将它放到片段中,这是活动:

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

    然后在片段中将监听器附加到调用它时:

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

    最后是听众:

    public interface changeFragmentListener {
    
        void onChange(boolean isNextFragment);
    
    }
    
  • 1

    在viewpager中更换片段非常复杂,但是非常有可能并且看起来非常光滑 . 首先,您需要让viewpager本身处理删除和添加片段 . 发生的事情是当您替换SearchFragment内部的片段时,您的viewpager会保留其片段视图 . 所以你最终得到一个空白页面,因为当你尝试替换它时会删除SearchFragment .

    解决方案是在viewpager内部创建一个侦听器,该侦听器将处理在其外部进行的更改,因此首先将此代码添加到适配器的底部 .

    public interface nextFragmentListener {
        public void fragment0Changed(String newFragmentIdentification);
    }
    

    然后,您需要在viewpager中创建一个私有类,该类在您想要更改片段时成为侦听器 . 例如,你可以添加这样的东西 . 请注意,它实现了刚刚创建的接口 . 因此,无论何时调用此方法,它都将运行下面类中的代码 .

    private final class fragmentChangeListener implements nextFragmentListener {
    
    
        @Override
        public void fragment0Changed(String fragment) {
            //I will explain the purpose of fragment0 in a moment
            fragment0 = fragment;
            manager.beginTransaction().remove(fragAt0).commit();
    
            switch (fragment){
                case "searchFragment":
                    fragAt0 = SearchFragment.newInstance(listener);
                    break;
                case "searchResultFragment":
                    fragAt0 = Fragment_Table.newInstance(listener);
                    break;
            }
    
            notifyDataSetChanged();
        }
    

    这里有两点需要指出:

    • fragAt0是"flexible"片段 . 它可以采用您提供的任何片段类型 . 这使它成为你最好的朋友,将位置0的片段更改为你想要的片段 .

    • 注意放在'newInstance(listener)构造函数中的侦听器 . 以下是callfragment0Changed(String newFragmentIdentification)`的方法 . 以下代码显示了如何在片段内创建侦听器 .

    static nextFragmentListener listenerSearch;

    public static Fragment_Journals newInstance(nextFragmentListener listener){
                listenerSearch = listener;
                return new Fragment_Journals();
            }
    

    您可以在 onPostExecute 内调用更改

    private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{
    
        protected Void doInBackground(Void... params){
            .
            .//some more operation
            .
        }
        protected void onPostExecute(Void param){
    
            listenerSearch.fragment0Changed("searchResultFragment");
        }
    
    }
    

    这将触发viewpager内部的代码将片段切换到零fragAt0,成为新的searchResultFragment . 在viewpager运行之前,还需要添加两个小块 .

    一个是在viewpager的getItem重写方法中 .

    @Override
    public Fragment getItem(int index) {
    
        switch (index) {
        case 0:
            //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  
    
            if(fragAt0 == null){
    
                switch(fragment0){
                case "searchFragment":
                    fragAt0 = FragmentSearch.newInstance(listener);
                    break;
                case "searchResultsFragment":
                    fragAt0 = FragmentSearchResults.newInstance(listener);
                    break;
                }
            }
            return fragAt0;
        case 1:
            // Games fragment activity
            return new CreateFragment();
    
        }
    

    现在没有这个最后一块,你仍然会得到一个空白页面 . 有点蹩脚,但它是viewPager的重要组成部分 . 您必须覆盖viewpager的getItemPosition方法 . 通常这个方法会返回POSITION_UNCHANGED,它告诉viewpager保持一切相同,因此getItem将永远不会被调用以将新片段放在页面上 . 这是你可以做的事情的一个例子

    public int getItemPosition(Object object)
    {
        //object is the current fragment displayed at position 0.  
        if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
            return POSITION_NONE;
        //this condition is for when you press back
        }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
            return POSITION_NONE;
        }
            return POSITION_UNCHANGED
    }
    

    就像我说的,代码非常复杂,但你基本上必须为你的情况创建一个自定义适配器 . 我提到的事情可以改变片段 . 浸泡所有东西可能需要很长时间,所以我会耐心等待,但这一切都有意义 . 完全值得花时间,因为它可以使一个非常光滑的应用程序 .

    这是处理后退按钮的金块 . 你把它放在你的MainActivity中

    public void onBackPressed() {
        if(mViewPager.getCurrentItem() == 0) {
            if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
                ((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
            }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
                finish();
            }
        }
    

    您需要在FragmentSearchResults内部创建一个名为backPressed()的方法,该方法调用fragment0changed . 这与我之前显示的代码一起将按下后退按钮 . 祝你好运用于更改viewpager的代码 . 它需要做很多工作,据我所知,没有任何快速的改编 . 就像我说的,你基本上是创建一个自定义viewpager适配器,并让它使用监听器处理所有必要的更改

  • 145

    tl;dr: 使用负责替换其托管内容的主机片段并跟踪后退导航历史记录(如在浏览器中) .

    由于您的用例包含固定数量的选项卡,因此我的解决方案运行良好:我们的想法是使用自定义类 HostFragment 的实例填充ViewPager,该实例能够替换其托管内容并保留其自己的后退导航历史记录 . 要替换托管片段,请调用方法 hostfragment.replaceFragment()

    public void replaceFragment(Fragment fragment, boolean addToBackstack) {
        if (addToBackstack) {
            getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
        } else {
            getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
        }
    }
    

    所有方法都是使用提供给方法的片段替换id R.id.hosted_fragment 的帧布局 .

    查看我的tutorial on this topic以获取更多详细信息和GitHub上的完整工作示例!

  • 31

    这是我实现这一目标的方法 .

    首先在 viewPager 选项卡中添加 Root_fragment ,在其中要实现按钮单击 fragment 事件 . 例;

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

    首先, RootTabFragment 应该包含 FragmentLayout 用于更改片段 .

    <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>
    

    然后,在 RootTabFragment onCreateView 内,为 FirstPagerFragment 实施 fragmentChange

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

    之后,为 FirstPagerFragment 中的按钮实现 onClick 事件,并再次进行片段更改 .

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

    希望这会帮助你的家伙 .

  • 6

    我按照@wize和@mdelolmo的回答,得到了解决方案 . 谢谢吨 . 但是,我稍微调整了这些解决方案以改善内存消耗 .

    我观察到的问题:

    它们保存了被替换的 Fragment 的实例 . 在我的情况下,它是一个片段,持有 MapView ,我认为它的代价高昂 . 所以,我维护 FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED) 而不是 Fragment 本身 .

    这是我的实施 .

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

    演示链接在这里.. https://youtu.be/l_62uhKkLyM

    出于演示目的,在位置2使用了2个片段 TabReplaceFragmentDemoTab2Fragment . 在所有其他情况下,我正在使用 DemoTabFragment 实例 .

    说明:

    我将 Switch 从Activity传递给 DemoCollectionPagerAdapter . 根据此开关的状态,我们将显示正确的片段 . 当切换检查改变时,我正在调用 SwitchFragListeneronSwitchToNextFragment 方法,我将 pagerAdapterPosChanged 变量的值更改为 POSITION_NONE . 详细了解POSITION_NONE . 这将使getItem无效,我有逻辑在那里实例化正确的片段 . 对不起,如果解释有点乱 .

    再次非常感谢@wize和@mdelolmo的最初想法 .

    希望这有用 . :)

    如果此实施有任何缺陷,请告诉我 . 这对我的项目非常有帮助 .

  • 0

    基于@wize的答案,我发现它有用且优雅,我可以实现我想要的部分,因为我希望能够在更换后回到第一个片段 . 我实现了一点修改他的代码 .

    这将是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();
    }
    

    要完成替换,只需定义一个类型为 CalendarPageFragmentListener 的静态字段,并通过相应片段的 newInstance 方法进行初始化,然后再调用 FirstFragment.pageListener.onSwitchToNextFragment()NextFragment.pageListener.onSwitchToNextFragment() .

    希望它清楚有用 .
    最好的祝福 .

  • 6

    一些提出的解决方案帮助我部分地解决了这个问题,但是在解决方案中仍然存在一个重要的缺陷,即在某些情况下产生了意外异常和黑页内容而不是片段内容 .

    问题是FragmentPagerAdapter类正在使用项ID来将缓存的片段存储到FragmentManager . 因此,您还需要覆盖getItemId(int position)方法,以便返回e . G . 顶级页面的位置和详细页面的100个位置 . 否则,先前创建的顶级片段将从缓存而不是细节级片段返回 .

    此外,我在这里分享一个完整的例子,说明如何使用ViewPager和使用RadioGroup的标签按钮实现与Fragment页面类似标签的活动,这些按钮允许用详细页面替换顶级页面,并且还支持后退按钮 . 此实现仅支持一个级别的后向堆栈(项目列表 - 项目详细信息),但多级后台堆叠实现很简单 . 此示例在正常情况下运行良好,除非在切换到e时抛出NullPointerException . G . 第二页,更改第一页的片段(虽然不可见),然后返回第一页 . 我'll post a solution to this issue once I'将弄明白:

    public class TabsActivity extends FragmentActivity {
    
      public static final int PAGE_COUNT = 3;
      public static final int FIRST_PAGE = 0;
      public static final int SECOND_PAGE = 1;
      public static final int THIRD_PAGE = 2;
    
      /**
       * Opens a new inferior page at specified tab position and adds the current page into back
       * stack.
       */
      public void startPage(int position, Fragment content) {
        // Replace page adapter fragment at position.
        mPagerAdapter.start(position, content);
      }
    
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // Initialize basic layout.
        this.setContentView(R.layout.tabs_activity);
    
        // Add tab fragments to view pager.
        {
          // Create fragments adapter.
          mPagerAdapter = new PagerAdapter(pager);
          ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
          pager.setAdapter(mPagerAdapter);
    
          // Update active tab in tab bar when page changes.
          pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int index, float value, int nextIndex) {
              // Not used.
            }
    
            @Override
            public void onPageSelected(int index) {
              RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
                R.id.tabs_radio_group);
              switch (index) {
                case 0: {
                  tabs_radio_group.check(R.id.first_radio_button);
                }
                break;
                case 1: {
                  tabs_radio_group.check(R.id.second_radio_button);
                }
                break;
                case 2: {
                  tabs_radio_group.check(R.id.third_radio_button);
                }
                break;
              }
            }
    
            @Override
            public void onPageScrollStateChanged(int index) {
              // Not used.
            }
          });
        }
    
        // Set "tabs" radio group on checked change listener that changes the displayed page.
        RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
        radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
          @Override
          public void onCheckedChanged(RadioGroup radioGroup, int id) {
            // Get view pager representing tabs.
            ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
            if (view_pager == null) {
              return;
            }
    
            // Change the active page.
            switch (id) {
              case R.id.first_radio_button: {
                view_pager.setCurrentItem(FIRST_PAGE);
              }
              break;
              case R.id.second_radio_button: {
                view_pager.setCurrentItem(SECOND_PAGE);
              }
              break;
              case R.id.third_radio_button: {
                view_pager.setCurrentItem(THIRD_PAGE);
              }
              break;
            }
          });
        }
      }
    
      @Override
      public void onBackPressed() {
        if (!mPagerAdapter.back()) {
          super.onBackPressed();
        }
      }
    
      /**
       * Serves the fragments when paging.
       */
      private class PagerAdapter extends FragmentPagerAdapter {
    
        public PagerAdapter(ViewPager container) {
          super(TabsActivity.this.getSupportFragmentManager());
    
          mContainer = container;
          mFragmentManager = TabsActivity.this.getSupportFragmentManager();
    
          // Prepare "empty" list of fragments.
          mFragments = new ArrayList<Fragment>(){};
          mBackFragments = new ArrayList<Fragment>(){};
          for (int i = 0; i < PAGE_COUNT; i++) {
            mFragments.add(null);
            mBackFragments.add(null);
          }
        }
    
        /**
         * Replaces the view pager fragment at specified position.
         */
        public void replace(int position, Fragment fragment) {
          // Get currently active fragment.
          Fragment old_fragment = mFragments.get(position);
          if (old_fragment == null) {
            return;
          }
    
          // Replace the fragment using transaction and in underlaying array list.
          // NOTE .addToBackStack(null) doesn't work
          this.startUpdate(mContainer);
          mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
            .remove(old_fragment).add(mContainer.getId(), fragment)
            .commit();
          mFragments.set(position, fragment);
          this.notifyDataSetChanged();
          this.finishUpdate(mContainer);
        }
    
        /**
         * Replaces the fragment at specified position and stores the current fragment to back stack
         * so it can be restored by #back().
         */
        public void start(int position, Fragment fragment) {
          // Remember current fragment.
          mBackFragments.set(position, mFragments.get(position));
    
          // Replace the displayed fragment.
          this.replace(position, fragment);
        }
    
        /**
         * Replaces the current fragment by fragment stored in back stack. Does nothing and returns
         * false if no fragment is back-stacked.
         */
        public boolean back() {
          int position = mContainer.getCurrentItem();
          Fragment fragment = mBackFragments.get(position);
          if (fragment == null) {
            // Nothing to go back.
            return false;
          }
    
          // Restore the remembered fragment and remove it from back fragments.
          this.replace(position, fragment);
          mBackFragments.set(position, null);
          return true;
        }
    
        /**
         * Returns fragment of a page at specified position.
         */
        @Override
        public Fragment getItem(int position) {
          // If fragment not yet initialized, create its instance.
          if (mFragments.get(position) == null) {
            switch (position) {
              case FIRST_PAGE: {
                mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
              }
              break;
              case SECOND_PAGE: {
                mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
              }
              break;
              case THIRD_PAGE: {
                mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
              }
              break;
            }
          }
    
          // Return fragment instance at requested position.
          return mFragments.get(position);
        }
    
        /**
         * Custom item ID resolution. Needed for proper page fragment caching.
         * @see FragmentPagerAdapter#getItemId(int).
         */
        @Override
        public long getItemId(int position) {
          // Fragments from second level page hierarchy have their ID raised above 100. This is
          // important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
          // this item ID key.
          Fragment item = mFragments.get(position);
          if (item != null) {
            if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
              (item instanceof NewThirdFragment)) {
              return 100 + position;
            }
          }
    
          return position;
        }
    
        /**
         * Returns number of pages.
         */
        @Override
        public int getCount() {
          return mFragments.size();
        }
    
        @Override
        public int getItemPosition(Object object)
        {
          int position = POSITION_UNCHANGED;
          if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
            if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
              position = POSITION_NONE;
            }
          }
          if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
            if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
              position = POSITION_NONE;
            }
          }
          if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
            if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
              position = POSITION_NONE;
            }
          }
          return position;
        }
    
        private ViewPager mContainer;
        private FragmentManager mFragmentManager;
    
        /**
         * List of page fragments.
         */
        private List<Fragment> mFragments;
    
        /**
         * List of page fragments to return to in onBack();
         */
        private List<Fragment> mBackFragments;
      }
    
      /**
       * Tab fragments adapter.
       */
      private PagerAdapter mPagerAdapter;
    }
    
  • 6

    我已经实施了一个解决方案:

    • 选项卡内的动态片段替换

    • 维护每个标签的历史记录

    • 使用方向更改

    实现这一目标的技巧如下:

    • 使用notifyDataSetChanged()方法应用片段替换

    • 仅将碎片管理器用于后台,不使用碎片更换

    • 使用 . 维护历史记录纪念品图案(堆栈)

    适配器代码如下:

    public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
    
    /** The sherlock fragment activity. */
    private final SherlockFragmentActivity mActivity;
    
    /** The action bar. */
    private final ActionBar mActionBar;
    
    /** The pager. */
    private final ViewPager mPager;
    
    /** The tabs. */
    private List<TabInfo> mTabs = new LinkedList<TabInfo>();
    
    /** The total number of tabs. */
    private int TOTAL_TABS;
    
    private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();
    
    /**
     * Creates a new instance.
     *
     * @param activity the activity
     * @param pager    the pager
     */
    public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        activity.getSupportFragmentManager();
        this.mActivity = activity;
        this.mActionBar = activity.getSupportActionBar();
        this.mPager = pager;
        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }
    
    /**
     * Adds the tab.
     *
     * @param image         the image
     * @param fragmentClass the class
     * @param args          the arguments
     */
    public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
        final TabInfo tabInfo = new TabInfo(fragmentClass, args);
        final ActionBar.Tab tab = mActionBar.newTab();
        tab.setTabListener(this);
        tab.setTag(tabInfo);
        tab.setIcon(image);
    
        mTabs.add(tabInfo);
        mActionBar.addTab(tab);
    
        notifyDataSetChanged();
    }
    
    @Override
    public Fragment getItem(final int position) {
        final TabInfo tabInfo = mTabs.get(position);
        return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
    }
    
    @Override
    public int getItemPosition(final Object object) {
        /* Get the current position. */
        int position = mActionBar.getSelectedTab().getPosition();
    
        /* The default value. */
        int pos = POSITION_NONE;
        if (history.get(position).isEmpty()) {
            return POSITION_NONE;
        }
    
        /* Checks if the object exists in current history. */
        for (Stack<TabInfo> stack : history.values()) {
            TabInfo c = stack.peek();
            if (c.fragmentClass.getName().equals(object.getClass().getName())) {
                pos = POSITION_UNCHANGED;
                break;
            }
        }
        return pos;
    }
    
    @Override
    public int getCount() {
        return mTabs.size();
    }
    
    @Override
    public void onPageScrollStateChanged(int arg0) {
    }
    
    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
    }
    
    @Override
    public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);
    }
    
    @Override
    public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
        TabInfo tabInfo = (TabInfo) tab.getTag();
        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i).equals(tabInfo)) {
                mPager.setCurrentItem(i);
            }
        }
    }
    
    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
    }
    
    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
    }
    
    public void replace(final int position, final Class fragmentClass, final Bundle args) {
        /* Save the fragment to the history. */
        mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
    
        /* Update the tabs. */
        updateTabs(new TabInfo(fragmentClass, args), position);
    
        /* Updates the history. */
        history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
    
        notifyDataSetChanged();
    }
    
    /**
     * Updates the tabs.
     *
     * @param tabInfo
     *          the new tab info
     * @param position
     *          the position
     */
    private void updateTabs(final TabInfo tabInfo, final int position) {
        mTabs.remove(position);
        mTabs.add(position, tabInfo);
        mActionBar.getTabAt(position).setTag(tabInfo);
    }
    
    /**
     * Creates the history using the current state.
     */
    public void createHistory() {
        int position = 0;
        TOTAL_TABS = mTabs.size();
        for (TabInfo mTab : mTabs) {
            if (history.get(position) == null) {
                history.put(position, new Stack<TabInfo>());
            }
            history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
            position++;
        }
    }
    
    /**
     * Called on back
     */
    public void back() {
        int position = mActionBar.getSelectedTab().getPosition();
        if (!historyIsEmpty(position)) {
            /* In case there is not any other item in the history, then finalize the activity. */
            if (isLastItemInHistory(position)) {
                mActivity.finish();
            }
            final TabInfo currentTabInfo = getPrevious(position);
            mTabs.clear();
            for (int i = 0; i < TOTAL_TABS; i++) {
                if (i == position) {
                    mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
                } else {
                    TabInfo otherTabInfo = history.get(i).peek();
                    mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
                }
            }
        }
        mActionBar.selectTab(mActionBar.getTabAt(position));
        notifyDataSetChanged();
    }
    
    /**
     * Returns if the history is empty.
     *
     * @param position
     *          the position
     * @return  the flag if empty
     */
    private boolean historyIsEmpty(final int position) {
        return history == null || history.isEmpty() || history.get(position).isEmpty();
    }
    
    private boolean isLastItemInHistory(final int position) {
        return history.get(position).size() == 1;
    }
    
    /**
     * Returns the previous state by the position provided.
     *
     * @param position
     *          the position
     * @return  the tab info
     */
    private TabInfo getPrevious(final int position) {
        TabInfo currentTabInfo = history.get(position).pop();
        if (!history.get(position).isEmpty()) {
            currentTabInfo = history.get(position).peek();
        }
        return currentTabInfo;
    }
    
    /** The tab info class */
    private static class TabInfo {
    
        /** The fragment class. */
        public Class fragmentClass;
    
        /** The args.*/
        public Bundle args;
    
        /**
         * Creates a new instance.
         *
         * @param fragmentClass
         *          the fragment class
         * @param args
         *          the args
         */
        public TabInfo(Class fragmentClass, Bundle args) {
            this.fragmentClass = fragmentClass;
            this.args = args;
        }
    
        @Override
        public boolean equals(final Object o) {
            return this.fragmentClass.getName().equals(o.getClass().getName());
        }
    
        @Override
        public int hashCode() {
            return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
        }
    
        @Override
        public String toString() {
            return "TabInfo{" +
                    "fragmentClass=" + fragmentClass +
                    '}';
        }
    }
    

    第一次添加所有选项卡时,我们需要调用createHistory()方法来创建初始历史记录

    public void createHistory() {
        int position = 0;
        TOTAL_TABS = mTabs.size();
        for (TabInfo mTab : mTabs) {
            if (history.get(position) == null) {
                history.put(position, new Stack<TabInfo>());
            }
            history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
            position++;
        }
    }
    

    每次要将片段替换为特定的选项卡时,都要调用:replace(final int position,final Class fragmentClass,final Bundle args)

    /* Save the fragment to the history. */
        mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
    
        /* Update the tabs. */
        updateTabs(new TabInfo(fragmentClass, args), position);
    
        /* Updates the history. */
        history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
    
        notifyDataSetChanged();
    

    在后退时你需要调用back()方法:

    public void back() {
        int position = mActionBar.getSelectedTab().getPosition();
        if (!historyIsEmpty(position)) {
            /* In case there is not any other item in the history, then finalize the activity. */
            if (isLastItemInHistory(position)) {
                mActivity.finish();
            }
            final TabInfo currentTabInfo = getPrevious(position);
            mTabs.clear();
            for (int i = 0; i < TOTAL_TABS; i++) {
                if (i == position) {
                    mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
                } else {
                    TabInfo otherTabInfo = history.get(i).peek();
                    mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
                }
            }
        }
        mActionBar.selectTab(mActionBar.getTabAt(position));
        notifyDataSetChanged();
    }
    

    该解决方案适用于sherlock操作栏和滑动手势 .

  • 4

    要替换 ViewPager 中的片段,您可以将 ViewPagerPagerAdapterFragmentStatePagerAdapter 类的源代码移动到项目中并添加以下代码 .

    进入 ViewPager

    public void notifyItemChanged(Object oldItem, Object newItem) {
        if (mItems != null) {
                for (ItemInfo itemInfo : mItems) {
                            if (itemInfo.object.equals(oldItem)) {
                                    itemInfo.object = newItem;
                            }
                        }
           }
           invalidate();
        }
    

    到FragmentStatePagerAdapter:

    public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
           startUpdate(container);
    
           // remove old fragment
    
           if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
           int position = getFragmentPosition(oldFragment);
            while (mSavedState.size() <= position) {
                mSavedState.add(null);
            }
            mSavedState.set(position, null);
            mFragments.set(position, null);
    
            mCurTransaction.remove(oldFragment);
    
            // add new fragment
    
            while (mFragments.size() <= position) {
                mFragments.add(null);
            }
            mFragments.set(position, newFragment);
            mCurTransaction.add(container.getId(), newFragment);
    
           finishUpdate(container);
    
           // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
           handleGetItemInbalidated(container, oldFragment, newFragment);
    
           container.notifyItemChanged(oldFragment, newFragment);
        }
    
    protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
    protected abstract int  getFragmentPosition(Fragment fragment);
    

    handleGetItemInvalidated() 确保在下次调用 getItem() 之后返回newFragment getFragmentPosition() 返回适配器中片段的位置 .

    现在,要替换片段调用

    mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);
    

    如果您对示例项目感兴趣,请向我询问消息来源 .

  • 1

    适用于AndroidTeam的解决方案,但是我发现我需要能够像_196055那样返回,但仅仅添加它只会导致片段被替换而不通知ViewPager . 将提供的解决方案与此次要增强功能相结合,将允许您仅通过覆盖活动的 onBackPressed() 方法返回到先前的状态 . 最大的缺点是它一次只能返回一个可能导致多次后退点击

    private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
    private ArrayList<Integer> bPosition = new ArrayList<Integer>();
    
    public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
        startUpdate(container);
    
        // remove old fragment
    
        if (mCurTransaction == null) {
             mCurTransaction = mFragmentManager.beginTransaction();
         }
        int position = getFragmentPosition(oldFragment);
         while (mSavedState.size() <= position) {
             mSavedState.add(null);
         }
    
         //Add Fragment to Back List
         bFragments.add(oldFragment);
    
         //Add Pager Position to Back List
         bPosition.add(position);
    
         mSavedState.set(position, null);
         mFragments.set(position, null);
    
         mCurTransaction.remove(oldFragment);
    
         // add new fragment
    
         while (mFragments.size() <= position) {
             mFragments.add(null);
         }
         mFragments.set(position, newFragment);
         mCurTransaction.add(container.getId(), newFragment);
    
        finishUpdate(container);
    
        // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
        handleGetItemInvalidated(container, oldFragment, newFragment);
    
        container.notifyItemChanged(oldFragment, newFragment);
     }
    
    
    public boolean popBackImmediate(ViewPager container){
        int bFragSize = bFragments.size();
        int bPosSize = bPosition.size();
    
        if(bFragSize>0 && bPosSize>0){
            if(bFragSize==bPosSize){
                int last = bFragSize-1;
                int position = bPosition.get(last);
    
                //Returns Fragment Currently at this position
                Fragment replacedFragment = mFragments.get(position);               
                Fragment originalFragment = bFragments.get(last);
    
                this.replaceFragments(container, replacedFragment, originalFragment);
    
                bPosition.remove(last);
                bFragments.remove(last);
    
                return true;
            }
        }
    
        return false;       
    }
    

    希望这有助于某人 .

    另外,就 getFragmentPosition() 来说,它的反面几乎是 getItem() . 你知道哪些碎片去了哪里,只要确保你返回它所在的正确位置 . 这是一个例子:

    @Override
        protected int getFragmentPosition(Fragment fragment) {
                if(fragment.equals(originalFragment1)){
                    return 0;
                }
                if(fragment.equals(replacementFragment1)){
                    return 0;
                }
                if(fragment.equals(Fragment2)){
                    return 1;
                }
            return -1;
        }
    

相关问题