29

我有一個活動,最初託管一個ViewPager,連接到FragmentPagerAdapter。用片段替換ViewPager - 然後回溯

當用戶單擊ViewPager的子片段內的項目時,我使用FragmentTransaction將一個空容器視圖替換爲我想要導航到的新Fragment。

如果我在事務上使用addToBackStack(),提交事務,然後導航回來,我沒有返回到ViewPager的視圖(初始佈局)。

如果我沒有在事務上使用addToBackStack(),請提交事務然後導航回來,應用程序退出。

似乎很明顯,ViewPager沒有添加到後臺堆棧(這並不令人驚訝,因爲它本身不是一個片段)。但我認爲默認行爲是後退將我帶回到那個活動初始查看(ViewPager)。

基於我讀過的內容,似乎可能是因爲片段事務正在發生,ViewPager或PagerAdapter會丟失應顯示哪個片段的軌道。

我真的很困惑,但我最終創造了一大堆代碼覆蓋onBackPress並顯示和隱藏viewpager視圖。我會認爲有一種更簡單的方法來使用默認行爲來執行適當的導航。

TL;博士

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> 

回答

3

我發現要做到這一點的唯一方法是執行以下操作:

當從viewPager導航時,使用Visiblity.GONE將viewPager發送出視圖。將任何碎片事務添加到堆棧。

當返回到viewPager屏幕(通過背按)時,覆蓋onBackPressed。您可以檢查後臺堆棧中有多少片段。如果viewPager是片段事務發生之前的第一個視圖,那麼您可以檢查片段背景入口計數是否爲0.

fragmentManager.getBackStackEntryCount()== 0,在backstack中沒有片段。

如果該語句爲真,那麼只需使用Visibility.VISIBLE將viewPager重新放入視圖即可。

+1

YES!謝謝。如果您ViewPager託管片段內,但如果該ViewPager託管一個FragmentActivity內 –

33

我也有很長一段時間這個非常相同的問題。該解決方案非常簡單,您無需使用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); 
+9

getChildFragmentManager()的罰款。 –

+0

不客氣:) –

9

UPDATE:
這不是 「Android的方式」,它會導致糟糕的用戶體驗ListView的情況下, 。相反,創建一個新的活動。

對於尋求這個問題的簡單解決方案的人,我只會總結我做了什麼。

我的架構:

  • ViewPager在FragmentActivity(ActionBarActivity實際上,對於動作條的支持,但ActionBarActivity實現FragmentActivity)。

  • 2標籤:

    • FragmentContainer1延伸片段。
    • 擴展片段的FragmentContainer2。

對於每個FragmentContainer,我們稱之爲getChildFragmentManager,在例如onCreate方法,並添加我們想在這個容器,顯示片段:

FragmentToShow fragment = new FragmentToShow(); 

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

我們不希望我們的第一個片段被添加到片段容器的堆棧中,因爲如果我們按下後退按鈕,我們不想顯示片段容器。

然後,如果我們想通過另一個片段中我們FragmentToShow類來代替FragmentToShow(像一個ListView):

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實例。

就是這樣!我不是專家,所以如果您有任何建議或評論,請放心。

+0

謝謝你這麼多onBackPressed()工作正常。 – Androidbirt

+0

這個架構正在爲我工​​作。謝謝:) – Dory

0

如果您使用的是含FragmentsViewPager可以啓動Activities,這裏是你如何正確地會說:Activity(假設你有申報向上導航導航回到位置在ViewPager,在回擊或瀏覽你的Activities)。

首先,您需要將ViewPager的當前位置作爲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(菜單項項)

+0

你的回答不清楚,請你給點意見或相關鏈接,謝謝你的貢獻。 –

0

我想我我有一個非常類似的問題。

似乎FragmentManager小號表現在一定程度分級方式。我已經解決這個問題的方法,是使用「主」 FragmentManager從活動,承載容器和ViewPager,而不是一個,可以從裏面ViewPager片段檢索。

要做到這一點,我用:

this.getActivity().getSupportFragmentManager() 

其中this是片段的一個實例。現在

,如果我從ViewPager一個項目轉到其他一些片段與「替換」的交易,回國後我可以看到ViewPager在我離開的狀態。

更完整的代碼示例如下:

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

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

transaction.commit();