在ViewPager中替换Fragment

我试图使用一个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,替换工作,但层次结构查看器显示一个奇怪的行为,如下所示。

在开始的情况是 < img src = " https://i.stack.imgur.com/29u9S.png " alt = " " > < / p >

替换后的情况是 < img src = " https://i.stack.imgur.com/6iMYc.png " alt = " " > < / p >

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

208492 次浏览

要替换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);

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

工作伟大的AndroidTeam的解决方案,但我发现我需要的能力回到像FrgmentTransaction.addToBackStack(null),但仅仅添加这只会导致片段被替换而不通知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;
}

还有另一种解决方案,不需要修改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();
}

希望这能帮助到大家!

一些提出的解决方案帮助我很多,部分解决问题,但仍有一个重要的事情,在解决方案中产生了意想不到的异常和黑页内容,而不是在某些情况下的片段内容。

问题是FragmentPagerAdapter类使用item ID将缓存的片段存储到FragmentManager。出于这个原因,你还需要重写getItemId (int位置)方法,以便它为顶级页面返回位置,为详细页面返回100 +位置。否则,之前创建的顶级片段将从缓存返回,而不是细节级片段。

此外,我在这里分享一个完整的示例,如何使用: viewpageRadioGroup使用片段页面实现类似选项卡的活动,选项卡按钮允许用详细页面替换顶级页面,还支持返回按钮。这个实现只支持一个级别的反向堆叠(项目列表-项目详细信息),但多层次的反向堆叠实现是直接的。这个例子在正常情况下工作得很好,除了当你切换到例如第二页,改变第一页的片段(当不可见时)并返回到第一页时抛出NullPointerException。一旦我弄清楚这个问题,我会发布一个解决方案:

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

基于@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()

下面是我对这个问题的相对简单的解决方案。这个解决方案的关键是使用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);
}
}

我也做了一个解决方案,这是与工作。这是一个更模块化的方法,所以你不必在你的FragmentPagerAdapter中指定每个Fragment和Detail Fragment。它建立在ActionbarSherlock的示例之上,如果我是对的,它来自谷歌演示应用程序。

/**
* 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中添加后退按钮功能:

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

如果你想保存Fragment状态当它被删除。让Fragment实现接口SaveStateBundle,在函数中返回一个包含保存状态的bundle。通过this.getArguments()获得实例化后的bundle。

你可以像这样实例化一个标签:

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

的工作类似于如果你想在一个标签堆栈的顶部添加一个片段。 重要的:我认为,如果你想在两个tab上有两个相同类的实例,这是行不通的。 我在一起很快就解决了这个问题,所以我只能分享它,不能提供任何经验

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

它非常类似于替换一个片段的正常方式,除了你使用getChildFragmentManager。当用户单击后退按钮时,除了嵌套的片段回堆栈不会弹出之外,它似乎正常工作。根据链接问题中的解决方案,您需要在片段的子管理器上手动调用popBackStackImmediate()。所以你需要重写ViewPager活动的onBackPressed(),你会得到ViewPager的当前片段,并调用getChildFragmentManager(). popbackstackimmediate()。

获取当前显示的片段也有点棘手,我使用了这个肮脏的“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;
}
}

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

  • 标签内的动态片段替换
  • 维护每个选项卡的历史记录
  • 处理方向变化

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

  • 使用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被按下时,你需要调用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();
}

解决方案与夏洛克动作栏和滑动手势。

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

替换viewpager中的片段是相当复杂的,但这是非常可能的,可以看起来超级光滑。首先,您需要让viewpager本身处理片段的删除和添加。发生的事情是,当你替换SearchFragment内部的片段时,你的viewpager保留它的片段视图。因此,您最终会得到一个空白页,因为当您试图替换它时,SearchFragment会被删除。

解决方案是在viewpager内部创建一个侦听器,它将处理在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();
}

这里主要有两点需要指出:

  1. fragAt0是一个“灵活的”片段。它可以接受你给它的任何片段类型。这使得它可以成为你最好的朋友,将位置0的片段更改为你想要的片段。
  2. 注意放在'newInstance(listener)构造函数中的侦听器。这就是你如何调用fragment0changed (String newFragmentIdentification) '。下面的代码展示了如何在片段中创建侦听器。

    静态nextFragmentListener;

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

You could call the change inside of your 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适配器,并让它使用侦听器处理所有必要的更改

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

enter image description here

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

ViewPagerChildFragments < / > < / p >

在你的onCreateView方法中,container实际上是一个ViewPager实例。

所以,只是打电话

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

将改变当前片段在你的ViewPager

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


}
经过研究,我找到了短代码的解决方案。 首先,在fragment上创建一个公共实例,如果fragment在方向改变时没有重新创建,则在onSaveInstanceState上删除你的fragment
 @Override
public void onSaveInstanceState(Bundle outState) {
if (null != mCalFragment) {
FragmentTransaction bt = getChildFragmentManager().beginTransaction();
bt.remove(mFragment);
bt.commit();
}
super.onSaveInstanceState(outState);
}

tl;博士:使用一个主机片段,负责替换它的托管内容,并跟踪回溯导航历史(就像在浏览器中)。

因为你的用例由固定数量的选项卡组成,我的解决方案工作得很好:这个想法是用自定义类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上查看我的关于此主题的教程以获得更多细节和完整的工作示例!

我跟着答案@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处使用了TabReplaceFragmentDemoTab2Fragment两个片段。在所有其他情况下,我使用DemoTabFragment实例。

解释:

我将Switch从活动传递给DemoCollectionPagerAdapter。基于这个开关的状态,我们将显示正确的片段。当切换检查被更改时,我调用SwitchFragListeneronSwitchToNextFragment方法,其中我将pagerAdapterPosChanged变量的值更改为POSITION_NONE。查看更多关于POSITION_NONE的信息。这将使getItem无效,我有逻辑实例化正确的片段在那里。抱歉,如果解释得有点乱的话。

再次感谢@wize和@mdelolmo的原创想法。

希望这对你有帮助。:)

如果这个实现有任何缺陷,请告诉我。那将对我的项目大有帮助。

这就是我实现目标的方法。

首先,在你想要实现按钮点击fragment事件的viewPager选项卡中添加Root_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();

希望这对你们有帮助。