简体   繁体   English

如何使用嵌套片段 (Android 4.2) 在 ViewPager 中添加片段

[英]How to add a Fragment inside a ViewPager using Nested Fragment (Android 4.2)

I have a ViewPager with three Fragments , each one shows a List (or Grid ).我有一个带有三个FragmentsViewPager ,每个都显示一个List (或Grid )。

In the new Android API level 17 (Jelly Bean 4.2), one of the features is Nested Fragments .在新的Android API level 17 (Jelly Bean 4.2)中,其中一项功能是Nested Fragments The new functionality description says:新功能描述说:

if you use ViewPager to create fragments that swipe left and right and consume a majority of the screen space, you can now insert fragments into each fragment page.如果您使用 ViewPager 创建向左和向右滑动并占用大部分屏幕空间的片段,您现在可以将片段插入到每个片段页面中。

So, if I understand right, now I can create a ViewPager with Fragments (with a button inside for example) inside, and when user press the button show another Fragment without loose the ViewPager using this new feature.所以,如果我理解正确,现在我可以创建一个带有Fragments (例如里面有一个按钮)的ViewPager ,并且当用户按下按钮时,使用这个新功能在不松开ViewPager情况下显示另一个Fragment

I've expended my morning trying to implement this several different ways, but I can´t get it working... Can somebody add a simple example of how to implement this?我已经花了我早上的时间试图以几种不同的方式实现这一点,但我无法让它工作......有人可以添加一个简单的例子来说明如何实现这一点吗?

PS: I'm only interested in doing at this way, with getChildFragmentManager to learn how works. PS:我只对这种方式感兴趣,用getChildFragmentManager来了解它是如何工作的。

Assuming you have created the correct xml layouts.假设您已经创建了正确的 xml 布局。 It is now very simple to display fragments in a ViewPager that is hosted by another Fragment.现在在由另一个 Fragment 托管的 ViewPager 中显示 Fragment 非常简单。

The following is a parent fragment that displays child fragments:以下是显示子片段的父片段:

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

It is important to use Fragment.getChildFragmentManager() when instantiating the FragmentPagerAdapter.在实例化 FragmentPagerAdapter 时使用Fragment.getChildFragmentManager()很重要。 Also note that you cannot use Fragment.setRetainInstance() on the children fragments or you'll get an exception.另请注意,您不能在子片段上使用Fragment.setRetainInstance() ,否则会出现异常。 The imports were omitted for brevity.为简洁起见,省略了导入。

Source code can be found at: https://github.com/marcoRS/nested-fragments源代码可在以下位置找到: https : //github.com/marcoRS/nested-fragments

Edit: If you want to replace all the content of a page in a ViewPager you could still use nested fragments, but some changes are needed.编辑:如果您想替换ViewPager页面的所有内容,您仍然可以使用嵌套片段,但需要进行一些更改。 Check the sample below(the FragmentActivity , setting the ViewPager and the PagerAdapter are the same as the previous snippet of code):检查下面的示例( FragmentActivity ,设置ViewPagerPagerAdapter与前面的代码片段相同):

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

At a quick look it works, but issues may appear as I haven't tested it to much.快速查看它的工作原理,但可能会出现问题,因为我还没有对其进行太多测试。

Can somebody show a simple example of how to do this?有人可以展示一个简单的例子来说明如何做到这一点吗?

Using nested fragments seems pretty easy, until Commonsware comes with a more elaborated sample you can try the code below:使用嵌套片段似乎很容易,直到 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;
        }

    }

}

I was lazy and made everything in code but I'm sure it can work with inflated xml layouts.我很懒惰,用代码编写了所有内容,但我确信它可以与膨胀的 xml 布局一起使用。

I have created a ViewPager with 3 elements and 2 sub elements for index 2 and 3 and here what I wanted to do..我创建了一个 ViewPager,其中包含 3 个元素和 2 个子元素,用于索引 2 和 3,这里是我想要做的。

在此处输入图片说明

I have implemented this with the help from previous questions and answers from StackOverFlow and here is the link.我已经在 StackOverFlow 之前的问题和答案的帮助下实现了这一点,这里是链接。

ViewPagerChildFragments ViewPagerChildFragments

Easily use Navigation Component in your app!在您的应用中轻松使用导航组件! then in your listener, copy this code:然后在您的侦听器中,复制此代码:

findNavController().navigate('Your action Id)
  • Your action id which IDE created automatically for you. IDE 自动为您创建的操作 ID。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM