【问题标题】:How to add a Fragment inside a ViewPager using Nested Fragment (Android 4.2)如何使用嵌套片段(Android 4.2)在 ViewPager 中添加片段
【发布时间】:2012-11-14 12:52:52
【问题描述】:

我有一个ViewPager 和三个Fragments,每个都显示一个List(或Grid)。

在新的Android API level 17(Jelly Bean 4.2)中,Nested Fragments 是其中一项功能。新的功能描述说:

如果您使用 ViewPager 创建左右滑动的片段 占用大部分屏幕空间,您现在可以插入片段 进入每个片段页面。

所以,如果我理解正确,现在我可以在里面创建一个带有Fragments(例如里面有一个按钮)的ViewPager,当用户按下按钮时显示另一个Fragment,而不会松开ViewPager使用这个新功能。

我花了一上午的时间尝试用几种不同的方式实现它,但我无法让它工作......有人可以添加一个简单的例子来说明如何实现这个吗?

PS:我只对这种方式感兴趣,通过getChildFragmentManager 了解如何工作。

【问题讨论】:

  • 我将在下周初为此制作一个样本。如果到那时还没有人回答,我会尝试在那个时候发布一些东西。

标签: android android-fragments android-viewpager android-support-library android-nested-fragment


【解决方案1】:

假设您已经创建了正确的 xml 布局。现在在另一个 Fragment 托管的 ViewPager 中显示 Fragment 非常简单。

以下是显示子片段的父片段:

class ParentViewPagerFragment : Fragment() {

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val root = inflater.inflate(R.layout.fragment_parent_viewpager, container, false)

    val viewPager = root.findViewById(R.id.viewPager) as ViewPager
    // Important: Must use the child FragmentManager or you will see side effects.
    viewPager.adapter = MyAdapter(childFragmentManager)

    val tabStrip = root.findViewById<TabLayout>(R.id.pagerTabStrip)
    tabStrip.setupWithViewPager(viewPager)

    return root
  }

  class MyAdapter internal constructor(fm: FragmentManager) : FragmentPagerAdapter(fm) {

    override fun getCount(): Int = 4

    override fun getItem(position: Int): Fragment {
      val args = Bundle().apply { putInt(ChildFragment.POSITION_KEY, position) }
      return ChildFragment.newInstance(args)
    }

    override fun getPageTitle(position: Int): CharSequence = "Tab $position"
  }

  companion object {
    val TAG: String = ParentViewPagerFragment::class.java.name
  }
}

在实例化 FragmentPagerAdapter 时使用 Fragment.getChildFragmentManager() 很重要。另请注意,您不能在子片段上使用 Fragment.setRetainInstance() ,否则会出现异常。为简洁起见,省略了导入。

源码可以在:https://github.com/marcoRS/nested-fragments

【讨论】:

  • 我在使用 SherlockFragments 和 support.v4 release11 时遇到问题(即包含 ViewPager 的片段),ViewPager 内容是通过适配器设置的,但是子片段不显示:(
  • 嗨斯特拉亚。我已经用 SherlockFragments 测试了我的示例,但没有看到错误。您看到的错误是什么?
  • 嘿,我还有 Android-ViewPagerIndicator。显示 ViewPager 和 ViewPagerIndicator(我在其 Fragment 中的 ViewPager 上方有一个 TextView,因此我可以看到显示了该 Fragment),但不显示 ViewPager 的子 Fragments(通过 FragmentPagerAdapter 提供)。我可以在页面之间滑动,指示器更新,但仍然不是子片段。
  • 发现我的 FragmentPagerAdapter 实现了自己的 isViewObject 方法,在将 ViewPager 嵌套在自己的片段中之前这不是问题,一旦删除,我现在看到页面 :) 拉出你的代码并运行它来提升我的信心和愿望让这个工作 - 所以谢谢马可!
  • @ChanjungKim 我已经更新了示例。完整代码可以在上面的 repo 链接中找到。
【解决方案2】:

编辑: 如果您想替换 ViewPager 中页面的所有内容,您仍然可以使用嵌套片段,但需要进行一些更改。查看下面的示例(FragmentActivity,设置ViewPagerPagerAdapter与之前的sn-p代码相同):

// this will act as a fragment container, representing one page in the ViewPager
public static class WrapperFragment extends Fragment implements
        ReplaceListener {

    public static WrapperFragment newInstance(int position) {
        WrapperFragment wp = new WrapperFragment();
        Bundle args = new Bundle();
        args.putInt("position", position);
        wp.setArguments(args);
        return wp;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        FrameLayout fl = new FrameLayout(getActivity());
        fl.setId(10000);
        if (getChildFragmentManager().findFragmentByTag("initialTag") == null) {
            InitialInnerFragment iif = new InitialInnerFragment();
            Bundle args = new Bundle();
            args.putInt("position", getArguments().getInt("position"));
            iif.setArguments(args);
            getChildFragmentManager().beginTransaction()
                    .add(10000, iif, "initialTag").commit();
        }
        return fl;
    }

    // required because it seems the getChildFragmentManager only "sees"
    // containers in the View of the parent Fragment.   
    @Override
    public void onReplace(Bundle args) {
        if (getChildFragmentManager().findFragmentByTag("afterTag") == null) {
            InnerFragment iif = new InnerFragment();
            iif.setArguments(args);
            getChildFragmentManager().beginTransaction()
                    .replace(10000, iif, "afterTag").addToBackStack(null)
                    .commit();
        }
    }

}

// the fragment that would initially be in the wrapper fragment
public static class InitialInnerFragment extends Fragment {

    private ReplaceListener mListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        mListener = (ReplaceListener) this.getParentFragment();
        LinearLayout ll = new LinearLayout(getActivity());
        Button b = new Button(getActivity());
        b.setGravity(Gravity.CENTER_HORIZONTAL);
        b.setText("Frame " + getArguments().getInt("position"));
        b.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Bundle args = new Bundle();
                args.putInt("positionInner",
                        getArguments().getInt("position"));
                if (mListener != null) {
                    mListener.onReplace(args);
                }
            }
        });
        ll.setOrientation(LinearLayout.VERTICAL);
        ll.addView(b, new LinearLayout.LayoutParams(250,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        return ll;
    }

}

public static class InnerFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        TextView tv = new TextView(getActivity());
        tv.setText("InnerFragment in the outher Fragment with position "
                + getArguments().getInt("positionInner"));
        return tv;
    }

}

public interface ReplaceListener {
    void onReplace(Bundle args);
}

快速查看它可以工作,但可能会出现问题,因为我没有对其进行过多测试。

有人可以举一个简单的例子来说明如何做到这一点吗?

使用嵌套片段似乎很容易,在 Commonsware 提供更详细的示例之前,您可以尝试以下代码:

public class NestedFragments extends FragmentActivity {

    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        ViewPager vp = new ViewPager(this);
        vp.setId(5000);
        vp.setAdapter(new MyAdapter(getSupportFragmentManager()));
        setContentView(vp);
    }

    private static class MyAdapter extends FragmentPagerAdapter {

        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return WrapperFragment.newInstance(position);
        }

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

    public static class WrapperFragment extends Fragment {

        public static WrapperFragment newInstance(int position) {
            WrapperFragment wp = new WrapperFragment();
            Bundle args = new Bundle();
            args.putInt("position", position);
            wp.setArguments(args);
            return wp;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            LinearLayout ll = new LinearLayout(getActivity());
            FrameLayout innerFragContainer = new FrameLayout(getActivity());
            innerFragContainer.setId(1111);
            Button b = new Button(getActivity());
            b.setText("Frame " + getArguments().getInt("position"));
            b.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    InnerFragment innerFragment = new InnerFragment();
                    Bundle args = new Bundle();
                    args.putInt("positionInner",
                            getArguments().getInt("position"));
                    innerFragment.setArguments(args);
                    FragmentTransaction transaction = getChildFragmentManager()
                            .beginTransaction();
                    transaction.add(1111, innerFragment).commit();
                }
            });
            ll.setOrientation(LinearLayout.VERTICAL);
            ll.addView(b, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT));
            ll.addView(innerFragContainer, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.MATCH_PARENT));
            return ll;
        }

    }

    public static class InnerFragment extends Fragment {

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            TextView tv = new TextView(getActivity());
            tv.setText("InnerFragment in the outher Fragment with position "
                    + getArguments().getInt("positionInner"));
            return tv;
        }

    }

}

我很懒,所有东西都用代码编写,但我确信它可以与膨胀的 xml 布局一起使用。

【讨论】:

  • 非常感谢,我正在尝试重构以与 XML 布局文件一起使用,并且似乎工作正常;)
  • 我认为你使用transaction.add(1111, innerFragment).commit(); 的地方应该使用replace 方法,因为每次按下按钮都会向层次结构添加一个布局。我还重构了示例以使用 XML 布局并尝试更改以替换 all 页面的内容。但我还没有做到,总是Button 保持领先。有什么建议吗?
  • @Luksprog:这不是我想的场景。我将研究一个示例,其中ViewPager 本身由片段托管,并且其页面具有片段。这应该只是将getChildFragmentManager() 传递给PagerAdapter 的问题,但我想先尝试一下。您的示例展示了一个 ViewPager 持有片段,这些片段本身持有其他片段 - 完全合理,只是不是我在阅读问题时的想法。
  • @sabadow 我以add 为例,您可以使用replace 它满足您的需求。 Button 保留在我的示例中,因为replace(或add) 方法不会从容器中删除现有视图(将innerFragContainer 的高度设置为WRAP_CONTENT,您会看到现有视图保留在底部添加新片段)。由于ViewPager 的工作方式,很难做出你想做的事情。明天我会找到解决方案。
  • @CommonsWare 我实际上希望用户需要。无论如何,我已经测试了将getChildFragmentManager 传递给PagerAdapter 设置为ViewPager 包含在Fragment 中,并且效果很好。
【解决方案3】:

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

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

ViewPagerChildFragments

【讨论】:

  • 此代码存在一些错误,例如它在更改方向时崩溃。你有没有修过它们?
  • 有没有人用这段代码修复了导致应用程序在方向更改时崩溃的错误?
【解决方案4】:

在您的应用中轻松使用导航组件!然后在你的监听器中,复制这段代码:

findNavController().navigate('Your action Id)
  • IDE 自动为您创建的操作 ID。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多