我试图使用片段与一个ViewPager使用FragmentPagerAdapter。 我想要实现的是替换一个片段,位于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);

        }
    }
}

...这里是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

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

?

如果我用r。id。first_fragment_root_id,替换工作,但Hierarchy Viewer显示一个奇怪的行为,如下所示。

一开始的情况是

更换后的情况是

正如你所看到的,有一些错误,我希望在替换片段后找到与第一张图片中相同的状态。


当前回答

我已经创建了一个ViewPager 3个元素和2个子元素的索引2和3和这里我想做的。

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

ViewPagerChildFragments

其他回答

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和提供给方法的片段。

查看我关于这个主题的教程,了解更多细节和GitHub上完整的工作示例!

我找到了一个简单的解决方案,即使你想在中间添加新的片段或替换当前片段,它也能很好地工作。在我的解决方案中,您应该重写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;
    }
}
}

注意:在这个例子中,FirstFragment和SecondFragment扩展了抽象类PageFragment,它有方法getPage()。

我做了一些类似于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);

}

要替换ViewPager中的片段,你可以将ViewPager、PagerAdapter和FragmentStatePagerAdapter类的源代码移动到你的项目中,并添加以下代码。

分为:viewpage:

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

如果你对一个示例项目感兴趣,请向我索取资源。

从2012年11月13日起,在ViewPager中重新调整片段似乎变得容易多了。谷歌发布的Android 4.2支持嵌套片段,在新的Android支持库v11中也支持它,所以这将一直工作到1.6

It's very similiar to the normal way of replacing a fragment except you use getChildFragmentManager. It seems to work except the nested fragment backstack isn't popped when the user clicks the back button. As per the solution in that linked question, you need to manually call the popBackStackImmediate() on the child manager of the fragment. So you need to override onBackPressed() of the ViewPager activity where you'll get the current fragment of the ViewPager and call getChildFragmentManager().popBackStackImmediate() on it.

获取当前正在显示的片段也有点棘手,我使用了这个肮脏的“android:switcher:VIEWPAGER_ID:INDEX”解决方案,但你也可以跟踪ViewPager的所有片段,正如本页上第二个解决方案所解释的那样。

这是我的代码,当用户单击一行时,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;
}
}