替换 ViewPager 中的 Fragment [英] Replace Fragment inside a ViewPager

查看:34
本文介绍了替换 ViewPager 中的 Fragment的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 on the first page of the ViewPager, with another one.

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

The pager is composed of two pages. The first one is the FirstPagerFragment, the second one is the SecondPagerFragment. Clicking on a button of the first page. I'd like to replace the FirstPagerFragment with the NextFragment.

下面是我的代码.

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

        }
    }
}

...这里是 xml 文件

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 中使用

Now the problem... which ID should I use in

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

?

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

If I use R.id.first_fragment_root_id, the replacement works, but Hierarchy Viewer shows a strange behavior, as below.

一开始的情况是

更换后的情况是

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

As you can see there is something wrong, I expect to find the same state shown as in the first picture after I replace the fragment.

推荐答案

还有一个解决方案,不需要修改ViewPagerFragmentStatePagerAdapter的源代码,也能用与作者使用的 FragmentPagerAdapter 基类.

There is another solution that does not need modifying source code of ViewPager and FragmentStatePagerAdapter, and it works with the FragmentPagerAdapter base class used by the author.

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

I'd like to start by answering the author's question about which ID he should use; it is ID of the container, i.e. ID of the view pager itself. However, as you probably noticed yourself, using that ID in your code causes nothing to happen. I will explain why:

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

First of all, to make ViewPager repopulate the pages, you need to call notifyDataSetChanged() that resides in the base class of your adapter.

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

Second, ViewPager uses the getItemPosition() abstract method to check which pages should be destroyed and which should be kept. The default implementation of this function always returns POSITION_UNCHANGED, which causes ViewPager to keep all current pages, and consequently not attaching your new page. Thus, to make fragment replacement work, getItemPosition() needs to be overridden in your adapter and must return POSITION_NONE when called with an old, to be hidden, fragment as argument.

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

This also means that your adapter always needs to be aware of which fragment that should be displayed in position 0, FirstPageFragment or NextFragment. One way of doing this is supplying a listener when creating FirstPageFragment, which will be called when it is time to switch fragments. I think this is a good thing though, to let your fragment adapter handle all fragment switches and calls to ViewPager and FragmentManager.

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

Third, FragmentPagerAdapter caches the used fragments by a name which is derived from the position, so if there was a fragment at position 0, it will not be replaced even though the class is new. There are two solutions, but the simplest is to use the remove() function of FragmentTransaction, which will remove its tag as well.

那是很多文本,以下是适用于您的情况的代码:

That was a lot of text, here is code that should work in your case:

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

希望对大家有帮助!

这篇关于替换 ViewPager 中的 Fragment的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆