用Fragment替换ViewPager - 然后返回导航

31

我有一个Activity,最初是使用FragmentPagerAdapter连接的ViewPager。

当用户在ViewPager的子片段中单击项目时,我使用FragmentTransaction将空容器视图替换为新的Fragment,以便进行导航。

如果在事务上使用addToBackStack(),提交事务然后进行导航回退,则不会返回到ViewPager的视图(初始布局)。

如果在事务上不使用addToBackStack(),提交事务然后导航回退,则应用程序会退出。

显然,ViewPager未添加到后退栈(这并不奇怪,因为它本身不是一个片段)......但是我希望默认行为是按返回键可以返回到活动的初始视图(即ViewPager)。

根据我所了解的,可能是由于发生了片段事务,ViewPager或PagerAdapter失去了应该显示的片段的跟踪。

我真的很困惑,但最终我创建了大量混乱的代码,覆盖了onBackPressed并显示和隐藏viewpager视图。我认为应该有一种更简单的方法来使用默认行为执行适当的导航。

tl; dr

A是托管片段的ViewPager。 B是一个新的片段。

当我用B替换A,然后按返回键,我期望导航回到A,但这并没有发生。

非常感谢任何建议。

代码:

MainActivity:

  @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

        headingLayout = (RelativeLayout) findViewById(R.id.headingLayout);
        headingLayout.setVisibility(View.GONE);

        // Set up the ViewPager, attaching the adapter and setting up a listener
        // for when the
        // user swipes between sections.
        mViewPager = (ViewPager) findViewById(R.id.pager);

        mViewPager.setPageMargin(8);

        /** Getting fragment manager */
        FragmentManager fm = getSupportFragmentManager();

        /** Instantiating FragmentPagerAdapter */
        MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

        /** Setting the pagerAdapter to the pager object */
        mViewPager.setAdapter(pagerAdapter);
.
.
.
}

    public void onListItemClicked(Fragment fragment) {
        fromPlayer = false;
        InitiateTransaction(fragment, true);

    }


    public void InitiateTransaction(Fragment fragment, boolean addToBackStack) {

        invalidateOptionsMenu();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();

        ft.replace(R.id.fragmentContainer, fragment).addToBackStack(null)
                .commit();

    }

PagerAdapter:

package another.music.player;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import another.music.player.fragments.AlbumListFragment;
import another.music.player.fragments.ArtistListFragment;
import another.music.player.fragments.SongListFragment;

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    final int PAGE_COUNT = 3;

    /** Constructor of the class */
    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    /** This method will be invoked when a page is requested to create */
    @Override
    public Fragment getItem(int i) {
        switch (i) {
        case 0:
            ArtistListFragment artistListFragment = new ArtistListFragment();
            Bundle artistData = new Bundle();
            artistData.putInt("current_page", i + 1);
            artistListFragment.setArguments(artistData);
            return artistListFragment;

        case 1:
            AlbumListFragment albumListFragment = new AlbumListFragment();
            Bundle albumData = new Bundle();
            albumData.putInt("current_page", i + 1);
            albumData.putBoolean("showHeader", false);
            albumListFragment.setArguments(albumData);
            return albumListFragment;

        default:

            SongListFragment songListFragment = new SongListFragment();
            Bundle songData = new Bundle();
            songData.putInt("current_page", i + 1);
            songListFragment.setArguments(songData);
            return songListFragment;
        }
    }

    /** Returns the number of pages */
    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
        case 0:
            return "Artists";

        case 1:
            return "Albums";

        default:
            return "Songs";
        }
    }
}

主xml文件(包含FragmentContainer和ViewPager):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/app_background_ics" >

    <RelativeLayout
        android:id="@+id/headingLayout"
        android:layout_width="match_parent"
        android:layout_height="56dp" >
    </RelativeLayout>

    <FrameLayout
        android:id="@+id/fragmentContainer"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/headingLayout" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <android.support.v4.view.PagerTabStrip
            android:id="@+id/pager_title_strip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#33b5e5"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:textColor="#fff" />
    </android.support.v4.view.ViewPager>

</RelativeLayout>
5个回答

43

我也长期遇到了这个同样的问题。解决方案非常简单,您不需要对ViewPager可见性进行任何修改。在这个相关的SO问题中有详细描述:Fragment in ViewPager not restored after popBackStack

然而,为了让它更简单,你只需要在你的ViewPager适配器中使用getChildFragmentManager(),而不是getSupportFragmentManager()。所以,替换成这个:

    /** Getting fragment manager */
    FragmentManager fm = getSupportFragmentManager();

    /** Instantiating FragmentPagerAdapter */
    MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

    /** Setting the pagerAdapter to the pager object */
    mViewPager.setAdapter(pagerAdapter);

你需要做这个:

    /** Getting fragment manager */
    FragmentManager fm = getChildFragmentManager();

    /** Instantiating FragmentPagerAdapter */
    MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);

    /** Setting the pagerAdapter to the pager object */
    mViewPager.setAdapter(pagerAdapter);

13
如果您的ViewPager位于Fragment内部,那么getChildFragmentManager()是可行的,但如果ViewPager位于FragmentActivity内部,则不行。 - Tim Malseed

9

更新:
这不是"Android方式",对于列表视图来说会导致糟糕的用户体验。相反,创建一个新活动。

对于寻找简单解决方案的人们,我将简要总结一下我的做法。

我的架构:

  • 在FragmentActivity中使用ViewPager(实际上是ActionBarActivity,用于支持ActionBar。但ActionBarActivity实现了FragmentActivity)。

  • 2个选项卡:

    • 扩展Fragment的FragmentContainer1。
    • 扩展Fragment的FragmentContainer2。

对于每个FragmentContainer,我们在onCreate方法中调用getChildFragmentManager,并添加我们想在此容器中显示的片段:

FragmentToShow fragment = new FragmentToShow();

getChildFragmentManager()
        .beginTransaction()
        .add(R.id.container, fragment)
        .commit();

我们不希望第一个片段添加到片段容器的返回栈中,因为如果我们按下返回按钮,我们不想显示片段容器。
然后,如果我们想在FragmentToShow类中(例如使用listView)替换FragmentToShow为另一个片段:
Fragment itemFragment = new ItemFragment();

getFragmentManager()
        .beginTransaction()
        .replace(R.id.container, itemFragment)
        .addToBackStack(null)
        .commit();

在这里,我们检索子片段管理器,并将itemFragment添加到返回堆栈中。

因此,现在我们希望,在按下返回按钮时返回到listView(FragmentToShow实例)。我们的活动(FragmentActivity)是唯一知道返回按钮的活动,因此我们必须在此活动中重写onBackPressed()方法:

@Override
public void onBackPressed() {

    // We retrieve the fragment manager of the activity
    FragmentManager frgmtManager = getSupportFragmentManager();

    // We retrieve the fragment container showed right now
    // The viewpager assigns tags to fragment automatically like this
    // mPager is our ViewPager instance
    Fragment fragment = frgmtManager.findFragmentByTag("android:switcher:" + mPager.getId() + ":" + mPager.getCurrentItem());

    // And thanks to the fragment container, we retrieve its child fragment manager
    // holding our fragment in the back stack
    FragmentManager childFragmentManager = fragment.getChildFragmentManager();

    // And here we go, if the back stack is empty, we let the back button doing its job
    // Otherwise, we show the last entry in the back stack (our FragmentToShow) 
    if(childFragmentManager.getBackStackEntryCount() == 0){
        super.onBackPressed();
    } else {
        childFragmentManager.popBackStack();
    }
}

由于我们在活动中调用了 getSupportFragmentManager,因此我们只能在子片段中调用 getFragmentManager。这将返回一个支持的 FragmentManager 实例。

就是这样!我不是专家,如果你有建议或意见,请自由提出。


这种架构对我很有帮助。谢谢 :) - Dory

3
我发现实现这个的唯一方法是按照以下步骤进行:
当从viewPager导航离开时,使用Visiblity.GONE将viewPager隐藏。将任何片段事务添加到后退栈中。
当通过返回按钮返回到viewPager屏幕时,重写onBackPressed。您可以检查后退栈中有多少个片段。如果viewPager在片段事务发生之前是第一个视图,则可以检查片段后退栈条目计数是否为0。
fragmentManager.getBackStackEntryCount() == 0,表示后退栈中没有片段。
如果该语句为真,则只需使用Visibility.VISIBLE将viewPager带回视图中即可。

0

我认为我遇到了非常相似的问题。

看起来FragmentManager的行为有点分层。我解决这个问题的方法是使用承载容器和ViewPager的活动中的“主”FragmentManager,而不是可以从ViewPager内部片段中检索的FragmentManager。

为此,我使用了:

this.getActivity().getSupportFragmentManager()

这里的this是一个Fragment实例。

现在,如果我从ViewPager中的某个项目导航到其他片段,并使用“replace”事务返回,我可以看到ViewPager处于我离开时的状态。

更完整的代码示例如下:

FragmentManager fragmentManager =  MyCurrentFragment.this.getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();

transaction.replace(R.id.container, newFragment);
transaction.addToBackStack(null);

transaction.commit();

0
如果您正在使用包含可以启动活动的片段的ViewPager,则以下是如何正确导航回到ViewPager中的位置,当从该Activity返回或向上导航时(假设您已为您的活动声明了向上导航)。
首先,您需要将ViewPager的当前位置作为Extra传递到Intent中以启动新的Activity。
然后,您将通过执行以下操作将该位置传回到父Activity:
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
upIntent.putExtra(FeedPagerActivity.EXTRA_POSITION, position);
NavUtils.navigateUpTo(this, upIntent);

将该代码放入

onOptionsItemSelected(MenuItem item)


你的回答不够清晰,能否请提供一些想法或相关链接?感谢您的贡献。 - MohanRaj S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接