29

Tôi đã có một hoạt động ban đầu lưu trữ một ViewPager, nối với FragmentPagerAdapter.Thay thế ViewPager bằng Fragment - Sau đó điều hướng Quay lại

Khi người dùng nhấp vào một mục bên trong đoạn con của ViewPager, tôi đang sử dụng FragmentTransaction để thay thế chế độ xem vùng chứa trống bằng Phân đoạn mới mà tôi muốn điều hướng đến.

Nếu tôi sử dụng addToBackStack() trên giao dịch, cam kết giao dịch và sau đó điều hướng trở lại, tôi không được trả về chế độ xem của ViewPager (bố cục ban đầu).

Nếu tôi không sử dụng addToBackStack() trên giao dịch, hãy cam kết giao dịch và sau đó điều hướng trở lại, ứng dụng sẽ thoát.

Có vẻ như rằng ViewPager không được thêm vào backstack (điều này không có gì đáng ngạc nhiên vì nó không phải là một phần của nó) .. Nhưng tôi mong đợi hành vi mặc định sẽ là báo chí quay lại đưa tôi trở lại cho hoạt động đó Chế độ xem ban đầu (ViewPager).

Dựa trên những gì tôi đã đọc, có vẻ như có lẽ do một giao dịch phân đoạn đang diễn ra, ViewPager hoặc PagerAdapter mất dấu vết phân đoạn nào sẽ được hiển thị.

Tôi thực sự bối rối với điều này, nhưng cuối cùng tôi đã tạo ra một mớ hỗn độn mã lớn ghi đè onBackPress và hiển thị và ẩn chế độ xem chế độ xem. Tôi đã nghĩ rằng có một cách đơn giản hơn để sử dụng các hành vi mặc định để thực hiện điều hướng thích hợp.

tl; dr

A là tập hợp lưu trữ Viewpager. B là một phân đoạn mới.

Khi tôi thay thế A bằng B, sau đó nhấn lại, tôi mong đợi điều hướng trở lại A, nhưng điều đó không xảy ra.

Mọi lời khuyên sẽ được đánh giá cao.

Code:

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 chính (chứa 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> 

Trả lời

3

Cách duy nhất tôi tìm thấy để đạt được điều này là làm như sau:

Khi điều hướng khỏi chế độ xemPager, hãy gửi viewPager ra khỏi chế độ xem bằng Visiblity.GONE. Thêm bất kỳ giao dịch phân đoạn nào vào hậu trường.

Khi quay lại màn hình xemPager (qua báo chí lại), ghi đè onBackPressed. Bạn có thể kiểm tra xem có bao nhiêu mảnh vỡ ở hậu trường. Nếu viewPager là chế độ xem đầu tiên trước khi các giao dịch phân đoạn diễn ra, thì bạn có thể kiểm tra xem số mục nhập backstack phân đoạn có phải là 0,

fragmentManager.getBackStackEntryCount() == 0, không có đoạn nào trong hậu trường.

Nếu tuyên bố đó là đúng, thì chỉ cần đưa viewPager trở lại chế độ xem bằng Visibility.VISIBLE.

+1

YES! Cảm ơn bạn. –

33

Tôi cũng gặp vấn đề này rất lâu trong một thời gian dài. Giải pháp hóa ra rất đơn giản, và bạn không cần bất kỳ hack nào với ViewPager Visibility. Tôi được mô tả trong câu hỏi liên quan đến SO khác: Fragment in ViewPager not restored after popBackStack

Tuy nhiên, để đơn giản, tất cả những gì bạn cần là sử dụng getChildFragmentManager() trong bộ điều hợp ViewPager, thay vì getSupportFragmentManager(). Vì vậy, thay vì điều này:

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

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

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

Bạn làm điều này:

/** 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() là tốt nếu ViewPager của bạn được lưu trữ bên trong một Fragment, nhưng không phải nếu ViewPager được lưu trữ bên trong một FragmentActivity. –

+0

Bạn được chào đón :) –

9

UPDATE:
Đó không phải là "cách Android" và nó kết quả trong kinh nghiệm của người dùng tồi tệ đối với trường hợp của một listview . Thay vào đó, hãy tạo một hoạt động mới.

Đối với những người đang tìm kiếm giải pháp đơn giản cho vấn đề này, tôi sẽ tóm tắt những gì tôi đã làm.

kiến ​​trúc của tôi:

  • ViewPager trong FragmentActivity (ActionBarActivity trên thực tế, hỗ trợ ActionBar Nhưng ActionBarActivity thực hiện FragmentActivity.).

  • 2 tab:

    • FragmentContainer1 kéo dài Fragment.
    • FragmentContainer2 mở rộng Phân đoạn.

Đối với mỗi FragmentContainer, chúng ta gọi là getChildFragmentManager, trong phương thức onCreate ví dụ, và thêm đoạn chúng ta muốn hiển thị trong vùng chứa này:

FragmentToShow fragment = new FragmentToShow(); 

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

Chúng tôi không muốn đầu tiên của chúng tôi mảnh được thêm vào backstack của container fragment bởi vì chúng ta không muốn hiển thị container fragment nếu chúng ta nhấn nút back.

Sau đó, nếu chúng ta muốn thay thế FragmentToShow bởi đoạn khác trong lớp FragmentToShow chúng tôi (như với một ListView):

Fragment itemFragment = new ItemFragment(); 

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

Ở đây chúng ta lấy người quản lý đoạn đứa trẻ, và chúng ta thêm itemFragment để ngăn xếp trở lại .

Vì vậy, bây giờ chúng tôi muốn, khi nhấn nút quay lại, để quay lại listView (ví dụ FragmentToShow). Hoạt động của chúng tôi (FragmentActivity) là người duy nhất nhận thức được những nút quay lại, vì vậy chúng ta phải ghi đè lên các phương pháp onBackPressed() trong hoạt động này:

@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(); 
    } 
} 

Kể từ khi chúng ta gọi là getSupportFragmentManager trong hoạt động của chúng tôi, chúng tôi chỉ có thể gọi getFragmentManager trong các mảnh con của chúng ta. Điều này sẽ trả về một cá thể FragmentManager hỗ trợ.

Và đó là nó! Tôi không phải là một chuyên gia, vì vậy nếu bạn có đề xuất hoặc nhận xét, cảm thấy tự do.

+0

Cảm ơn bạn rất nhiều onBackPressed() hoạt động tốt. – Androidbirt

+0

Kiến trúc này hoạt động với tôi. Cảm ơn bạn :) – Dory

0

Nếu bạn đang sử dụng một ViewPager chứa Fragments có thể bắt đầu Activities, đây là cách bạn sẽ đúng hướng trở lại vị trí trong ViewPager, sau khi nảy lại hoặc điều hướng tăng so với nói Activity (giả sử bạn có navigation Trở lên tuyên bố cho bạn Activities).

Trước tiên, bạn cần phải vượt qua vị trí hiện tại của ViewPager làm Phụ trang trong số Intent để bắt đầu Activity mới.

Sau đó, bạn sẽ vượt qua trở lại vị trí đó cho phụ huynh Activity làm điều này:

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

Đặt mã rằng trong vòng

onOptionsItemSelected (MenuItem item)

+0

Câu trả lời của bạn không rõ ràng, bạn có thể vui lòng đưa ra một số Ý tưởng hoặc liên kết có liên quan, Cảm ơn bạn đã đóng góp. –

0

Tôi nghĩ tôi đã có một vấn đề rất giống nhau.

Dường như FragmentManager hoạt động theo cách phân cấp một phần. Cách tôi đã giải quyết vấn đề này, là sử dụng FragmentManager "chính" từ hoạt động, lưu trữ vùng chứa và ViewPager, chứ không phải là tệp, có thể được truy xuất từ ​​các đoạn trong ViewPager.

Để làm điều này, tôi đã sử dụng:

this.getActivity().getSupportFragmentManager() 

nơi this là một thể hiện của Fragment.

Bây giờ nếu tôi điều hướng từ một mục trong ViewPager sang một đoạn khác với giao dịch "thay thế", khi trở về, tôi có thể thấy ViewPager ở trạng thái mà tôi đã để lại.

hoàn chỉnh hơn mẫu mã trông như thế này:

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

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

transaction.commit();