Làm cách nào tôi có thể duy trì trạng thái phân đoạn khi thêm vào ngăn xếp phía sau?


160

Tôi đã viết lên một hoạt động giả chuyển đổi giữa hai mảnh. Khi bạn đi từ FragmentA đến FragmentB, FragmentA sẽ được thêm vào ngăn xếp phía sau. Tuy nhiên, khi tôi trở lại FragmentA (bằng cách nhấn lại), một FragmentA hoàn toàn mới được tạo ra và trạng thái của nó bị mất. Tôi có cảm giác tôi giống như câu hỏi này , nhưng tôi đã bao gồm một mẫu mã hoàn chỉnh để giúp giải quyết vấn đề:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Với một số thông điệp tường trình được thêm vào:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Nó không bao giờ gọi FragmentA.onSaveInstanceState và nó tạo ra một FragmentA mới khi bạn quay lại. Tuy nhiên, nếu tôi ở trên FragmentA và tôi khóa màn hình, FragmentA.onSaveInstanceState sẽ được gọi. Thật kỳ lạ ... tôi có sai không khi mong đợi một đoạn được thêm vào ngăn xếp phía sau để không cần tạo lại? Đây là những gì các tài liệu nói:

Trong khi đó, nếu bạn gọi addToBackStack () khi xóa một đoạn, thì đoạn đó sẽ bị dừng và sẽ được nối lại nếu người dùng điều hướng trở lại.


3
@ Jan-Henk Còn những thứ phải lấy? Chẳng hạn, vị trí cuộn của a ListView. Có vẻ như nhảy quá nhiều để gắn một trình nghe cuộn và cập nhật một biến thể hiện.
Jake Wharton

2
@JakeWharton Tôi đồng ý rằng nó sẽ dễ dàng hơn, nhưng theo tôi biết thì không có cách nào khác vì onCreateView được gọi khi một đoạn được khôi phục từ backstack. Nhưng tôi có thể sai :)
Jan-Henk

1
onCreate không được gọi. Vì vậy, rõ ràng là nó đang sử dụng lại cùng một thể hiện nhưng lại gọi onCreateView? Què. Tôi đoán tôi chỉ có thể lưu trữ kết quả của onCreateView và chỉ trả về chế độ xem hiện có nếu onCreateView được gọi lại.
Eric

1
Chính xác những gì tôi đang tìm kiếm hàng giờ. Bạn có thể gửi làm thế nào bạn đạt được điều này bằng cách sử dụng biến thể?
Uma

1
Vì vậy, gần đây tôi đã bắt đầu triển khai của riêng mình trong github.com/frostymarvelous/Folio và gặp phải một vấn đề. Tôi có thể tạo khoảng 5 Trang / Mảnh phức tạp trước khi bắt đầu gặp sự cố OOM. Đó là những gì dẫn tôi đến đây. Ẩn và hiển thị chỉ đơn giản là không đủ. Lượt xem quá nặng ký ức.
băng giá

Câu trả lời:


120

Nếu bạn quay lại một đoạn từ ngăn xếp phía sau, nó không tạo lại đoạn đó mà sử dụng lại cùng một thể hiện và bắt đầu onCreateView()trong vòng đời của đoạn, xem vòng đời của đoạn .

Vì vậy, nếu bạn muốn lưu trữ trạng thái, bạn nên sử dụng các biến thể hiện và không dựa vào onSaveInstanceState().


32
Phiên bản hiện tại của tài liệu mâu thuẫn với tuyên bố này. Lưu đồ cho biết những gì bạn nêu, nhưng văn bản trong khu vực chính của trang cho biết onCreateView () chỉ được gọi lần đầu tiên Fragment được hiển thị: developer.android.com/guide/components/fragments.html Tôi đang chiến đấu với điều này vấn đề bây giờ và tôi không thấy bất kỳ phương thức nào được gọi khi trả lại một đoạn từ backstack. (Android 4.2)
Colin M.

10
Đã thử để đăng nhập hành vi của nó. OnCreateView () luôn được gọi khi đoạn được hiển thị.
princepiero

4
@ColinM. Bất kỳ giải pháp cho vấn đề?
bão tuyết

9
Điều này không làm việc cho tôi. Các biến thể hiện của tôi là null khi quay lại đoạn! Làm thế nào tôi có thể cứu nhà nước?
Don Rhummy

5
Vì vậy, nếu chúng ta không nên chuyển tiếp vào ví dụ lưu, làm thế nào nên lưu trạng thái và dữ liệu đoạn?
Mahdi

80

So sánh với Apple UINavigationControllerUIViewController, Google không làm tốt về kiến ​​trúc phần mềm Android. Và tài liệu của Android về Fragmentkhông giúp được gì nhiều.

Khi bạn nhập FragmentB từ FragmentA, phiên bản FragmentA hiện tại không bị hủy. Khi bạn nhấn Back in FragmentB và quay lại FragmentA, chúng tôi sẽ không tạo một cá thể FragmentA mới. Ví dụ của FragmentA hiện tại onCreateView()sẽ được gọi.

Điều quan trọng là chúng ta không nên tăng cường xem lại trong FragmentA onCreateView(), bởi vì chúng ta đang sử dụng thể hiện của FragmentA. Chúng ta cần lưu và sử dụng lại rootView.

Các mã sau hoạt động tốt. Nó không chỉ giữ trạng thái phân mảnh, mà còn giảm tải RAM và CPU (vì chúng tôi chỉ tăng bố cục nếu cần thiết). Tôi không thể tin rằng mã mẫu và tài liệu của Google không bao giờ đề cập đến nó nhưng luôn làm tăng bố cục .

Phiên bản 1 (Không sử dụng phiên bản 1. Sử dụng phiên bản 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Cập nhật ngày 3 tháng 5 năm 2005: -------

Như các ý kiến ​​đã đề cập, đôi khi _rootView.getParent()là null onCreateView, điều này gây ra sự cố. Phiên bản 2 xóa _rootView trong onDestroyView (), như dell116 đề xuất. Đã thử nghiệm trên Android 4.0.3, 4.4.4, 5.1.0.

Phiên bản 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

CẢNH BÁO!!!

Đây là một HACK! Mặc dù tôi đang sử dụng nó trong ứng dụng của mình, bạn cần kiểm tra và đọc các bình luận cẩn thận.


38
Giữ một tài liệu tham khảo cho toàn bộ rootview là một ý tưởng tồi IMO. Nếu bạn liên tục thêm một số đoạn vào backstack và tất cả chúng đều giữ rootview của nó (có dấu chân bộ nhớ khá lớn), thì có khả năng cao là bạn sẽ kết thúc với OutOfMemoryError vì tất cả các đoạn đó đều có tham chiếu rootview và GC cant thu thập nó Tôi nghĩ rằng cách tiếp cận tốt hơn là làm tăng tầm nhìn mọi lúc (và để hệ thống Android xử lý việc tạo / hủy chế độ xem của nó) và kiểm tra onActivityCreated / onViewCreated nếu dữ liệu của bạn là null. Nếu có, sau đó tải nó, khác đặt dữ liệu thành chế độ xem.
traninho

15
Đừng làm điều này! Khi hệ thống phân cấp khung nhìn của Fragment được tạo, nó chứa tham chiếu bên trong tới Activity giữ đoạn đó tại thời điểm đó. Khi thay đổi cấu hình xảy ra, Activity thường được tạo lại. Việc sử dụng lại bố cục cũ sẽ giữ hoạt động của zombie xung quanh trong bộ nhớ cùng với bất kỳ đối tượng nào mà nó cũng tham chiếu. Lãng phí bộ nhớ như thế này cản trở hiệu suất và làm cho ứng dụng của bạn trở thành ứng cử viên hàng đầu để chấm dứt ngay lập tức khi không ở phía trước.
Krylez

4
@ ALLDayAmazed Đây là một điểm tốt. Thành thật mà nói, tôi rất bối rối ngay bây giờ. Bất cứ ai cũng có thể cố gắng giải thích tại sao việc giữ một tham chiếu đến rootview của một đoạn không ổn nhưng chỉ giữ một tham chiếu cho bất kỳ đứa trẻ nào của rootview (dù sao cũng có tham chiếu đến rootview) thì không sao?
traninho

2
HÃY THAY THẾ TỪ NÀY trừ khi bạn muốn lãng phí 5 giờ để tìm hiểu những gì làm mất mã của mình ..... sau đó chỉ để thấy rằng đây là nguyên nhân. Bây giờ tôi phải cấu trúc lại một loạt các công cụ vì tôi đã sử dụng bản hack này. Sẽ tốt hơn nhiều khi sử dụng FragmentTransaction.add nếu bạn muốn giữ giao diện người dùng của đoạn đó không hoạt động khi đưa người khác vào xem (ngay cả trên đầu trang). FragmentTransaction.replace () có nghĩa là để phá hủy các quan điểm của đoạn ..... đừng chống lại hệ thống.
dell116

2
@VinceYuan - Tôi đã thử nghiệm với thư viện v7-appcompat mới nhất trên Android 5.1 và điều này đã để lại 6 trường hợp của một đoạn nên đã bị xóa trong FragmentManager của hoạt động của tôi. Ngay cả khi GC sẽ xử lý chính xác (điều mà tôi không tin), điều này sẽ gây ra sự căng thẳng không cần thiết cho bộ nhớ cho ứng dụng của bạn cũng như thiết bị nói chung. Chỉ cần sử dụng .add () sẽ loại bỏ hoàn toàn sự cần thiết của tất cả các mã hack này. Làm điều này là hoàn toàn chống lại những gì sử dụng FragmentTransaction.replace () có nghĩa là làm ở nơi đầu tiên.
dell116

53

Tôi đoán có một cách khác để đạt được những gì bạn đang tìm kiếm. Tôi không nói đó là một giải pháp hoàn chỉnh nhưng nó phục vụ mục đích trong trường hợp của tôi.

Những gì tôi đã làm là thay vì thay thế đoạn tôi chỉ thêm đoạn đích. Vì vậy, về cơ bản, bạn sẽ sử dụng add()phương pháp thay thế replace().

Những gì tôi đã làm. Tôi giấu đoạn hiện tại của mình và cũng thêm nó vào backstack.

Do đó, nó chồng lấp lên đoạn mới so với đoạn hiện tại mà không phá hủy khung nhìn của nó (kiểm tra xem onDestroyView()phương thức của nó không được gọi. Thêm vào đó để backstatemang lại cho tôi lợi thế của việc nối lại đoạn đó.

Đây là mã:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

Hệ thống AFAIK chỉ gọi onCreateView()nếu chế độ xem bị hủy hoặc không được tạo. Nhưng ở đây chúng tôi đã lưu chế độ xem bằng cách không xóa nó khỏi bộ nhớ. Vì vậy, nó sẽ không tạo ra một cái nhìn mới.

Và khi bạn quay trở lại từ Destination Fragment, nó sẽ hiện ra đoạn cuối FragmentTransactionloại bỏ cuối cùng sẽ làm cho chế độ xem trên cùng (SourceFragment) xuất hiện trên màn hình.

NHẬN XÉT: Như tôi đã nói, đây không phải là một giải pháp hoàn chỉnh vì nó không loại bỏ chế độ xem Đoạn nguồn và do đó chiếm nhiều bộ nhớ hơn bình thường. Nhưng vẫn, phục vụ mục đích. Ngoài ra, chúng tôi đang sử dụng một cơ chế ẩn hoàn toàn khác thay vì thay thế nó không truyền thống.

Vì vậy, nó không thực sự là cách bạn duy trì trạng thái, mà là cách bạn duy trì quan điểm.


Trong trường hợp của tôi, bằng cách thêm một đoạn thay vì thay thế gây ra vấn đề khi sử dụng bỏ phiếu hoặc bất kỳ loại yêu cầu web nào được sử dụng trong đoạn đó. Tôi muốn tạm dừng cuộc bỏ phiếu này trong Đoạn A khi Đoạn B được thêm vào. Bất cứ ý tưởng về điều này?
Uma

Làm thế nào bạn đang sử dụng bỏ phiếu trong FirstFragment? Bạn phải làm điều đó một cách thủ công vì cả hai mảnh vẫn còn trong bộ nhớ. Vì vậy, bạn có thể sử dụng các thể hiện của chúng để thực hiện hành động cần thiết. Đó là đầu mối, tạo ra một sự kiện trong hoạt động chính làm gì đó khi bạn thêm đoạn thứ hai. Hy vọng điều này sẽ giúp.
kaushal trivingi

1
Cảm ơn bạn đã đầu mối =). Tôi đã làm điều này. Nhưng đây có phải là cách duy nhất để làm điều đó? Và một cách thích hợp? Ngoài ra Khi tôi nhấn nút home và khởi chạy lại ứng dụng, tất cả các mảnh sẽ hoạt động trở lại. Giả sử Im ở đây trong Fragment B thông qua cách này. Activity A{Fragment A --> Fragment B}Khi tôi khởi chạy lại ứng dụng sau khi nhấn nút home, cả hai đoạn onResume()được gọi và do đó chúng bắt đầu bỏ phiếu. Làm thế nào tôi có thể kiểm soát điều này?
Uma

1
Thật không may là bạn không thể, Hệ thống không hoạt động bình thường theo cách này, nó sẽ coi cả hai phân đoạn là con trực tiếp của hoạt động. Nó phục vụ mục đích duy trì trạng thái phân mảnh, những thứ bình thường khác rất khó quản lý. đã phát hiện ra tất cả các vấn đề này, bây giờ đề nghị của tôi là không đi theo cách này. Xin lỗi.
kaushal trivingi

1
Tất nhiên, cuối cùng tôi sẽ nói không đi theo cách tiếp cận này cho đến khi bạn tìm thấy bất kỳ giải pháp nào khác. Bởi vì nó khó quản lý.
kaushal trivingi

7

Tôi sẽ đề nghị một giải pháp rất đơn giản.

Lấy biến tham chiếu View và đặt chế độ xem trong OnCreateView. Kiểm tra xem chế độ xem đã tồn tại trong biến này chưa, sau đó trả về cùng một chế độ xem.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }

1
Có khả năng bị rò rỉ bộ nhớ nếu chúng tôi không xóa biến 'FragmentView' trong onDestroy ()
Arun PM

@ArunPM vậy làm cách nào để loại bỏ FragmentView trong onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }Có thích hợp để xóa trí nhớ?
Mehmet Gür

1
@ MehmetGür tôi đang sử dụng giải pháp này nhiều lần. Cho đến bây giờ tôi đã không nhận được bất kỳ lỗi rò rỉ bộ nhớ. Nhưng bạn có thể sử dụng giải pháp ArunPM với điều đó nếu bạn muốn. Tôi nghĩ anh ấy đang bảo đặt FragmentView thành null trong Phương thức OnDestroy ().
Mandeep Singh

1
Tôi đang sử dụng LeakCanary để phát hiện rò rỉ bộ nhớ và các vấn đề rò rỉ khi sử dụng phương pháp này. Nhưng như @Mandeep Sigh đã đề cập trong bình luận, chúng ta có thể khắc phục vấn đề này bằng cách gán nullcho fragmentView biến trong onDestroy()phương thức.
Arun PM

1
Theo hiểu biết của tôi, khi một mảnh bị phá hủy, khung nhìn liên quan đến mảnh vỡ sẽ bị xóa onDestroyView(). Việc xóa này không xảy ra đối với biến xem dự phòng của chúng tôi (ở đây fragmentView ) và nó sẽ gây rò rỉ bộ nhớ khi đoạn bị xếp lại / bị phá hủy. Bạn có thể tìm thấy cùng một tham chiếu trong [Nguyên nhân phổ biến gây rò rỉ bộ nhớ] ( vuông.github.io/leakcanary/fundamentals/ trộm ) trong phần giới thiệu LeakCanery.
Arun PM

6

Tôi đã gặp vấn đề này trong một mảnh chứa bản đồ, có quá nhiều chi tiết thiết lập để lưu / tải lại. Giải pháp của tôi là về cơ bản giữ cho mảnh vỡ này hoạt động suốt thời gian (tương tự như những gì @kaushal đã đề cập).

Giả sử bạn có Đoạn A hiện tại và muốn hiển thị Đoạn B. Tóm tắt hậu quả:

  • thay thế () - xóa Fragment A và thay thế nó bằng Fragment B. Fragment A sẽ được tạo lại sau khi đưa ra phía trước một lần nữa
  • thêm () - (tạo và) thêm một đoạn B và nó chồng lên đoạn A, vẫn đang hoạt động ở chế độ nền
  • remove () - có thể được sử dụng để xóa Fragment B và quay lại A. Fragment B sẽ được tạo lại khi được gọi sau này

Do đó, nếu bạn muốn giữ cả hai Đoạn "được lưu", chỉ cần chuyển đổi chúng bằng cách sử dụng ẩn () / show ().

Ưu điểm : phương pháp dễ dàng và đơn giản để duy trì nhiều phân đoạn chạy
Nhược điểm : bạn sử dụng nhiều bộ nhớ hơn để duy trì tất cả chúng chạy. Có thể gặp sự cố, ví dụ: hiển thị nhiều ảnh bitmap lớn


bạn có thể vui lòng cho tôi biết khi chúng tôi xóa đoạn b và quay lại A thì phương thức nào được gọi trong Đoạn A không? tôi muốn thực hiện một số hành động khi chúng tôi xóa đoạn B.
Google

5

onSaveInstanceState() chỉ được gọi nếu có thay đổi cấu hình.

Do thay đổi từ mảnh này sang mảnh khác nên không có thay đổi cấu hình nên không có cuộc gọi nào onSaveInstanceState() ở đó. Nhà nước nào không được cứu? Bạn có thể chỉ định?

Nếu bạn nhập một số văn bản trong EditText, nó sẽ được lưu tự động. Bất kỳ mục UI nào không có ID là mục có trạng thái xem sẽ không được lưu.


onSaveInstanceState()cũng được gọi khi hệ thống phá hủy Activity vì nó thiếu tài nguyên.
Marcel Bro

0

Ở đây, vì onSaveInstanceStatetrong đoạn không gọi khi bạn thêm đoạn vào backstack. Vòng đời của đoạn trong backstack khi được khôi phục bắt đầu onCreateViewvà kết thúc onDestroyViewtrong khi onSaveInstanceStateđược gọi giữa onDestroyViewonDestroy. Giải pháp của tôi là tạo biến thể hiện và init in onCreate. Mã mẫu:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

Và kiểm tra xem onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}

0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

0

Vấn đề của tôi là tương tự nhưng tôi đã vượt qua tôi mà không giữ mảnh vỡ còn sống. Giả sử bạn có một hoạt động có 2 đoạn - F1 và F2. F1 được bắt đầu ban đầu và giả sử có chứa một số thông tin người dùng và sau đó với một số điều kiện F2 xuất hiện khi yêu cầu người dùng điền vào thuộc tính bổ sung - số điện thoại của họ. Tiếp theo, bạn muốn số điện thoại đó bật trở lại F1 và hoàn tất đăng ký nhưng bạn nhận ra tất cả thông tin người dùng trước đó bị mất và bạn không có dữ liệu trước đó của họ. Đoạn được tạo lại từ đầu và ngay cả khi bạn đã lưu thông tin này trong onSaveInstanceStategói sẽ trở lại null onActivityCreated.

Giải pháp: Lưu thông tin bắt buộc dưới dạng một biến thể hiện trong hoạt động gọi. Sau đó chuyển biến thể hiện đó vào đoạn của bạn.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

Vì vậy, tiếp theo với ví dụ của tôi: trước khi tôi hiển thị F2, tôi lưu dữ liệu người dùng trong biến thể hiện bằng cách sử dụng một cuộc gọi lại. Sau đó, tôi bắt đầu F2, người dùng điền vào số điện thoại và nhấn lưu. Tôi sử dụng một cuộc gọi lại khác trong hoạt động, thu thập thông tin này và thay thế đoạn F1 của tôi, lần này là dữ liệu bó mà tôi có thể sử dụng.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Thông tin thêm về các cuộc gọi lại có thể được tìm thấy ở đây: https://developer.android.com/training/basics/fragments/cransicating.html


0

Đầu tiên : chỉ cần sử dụng phương thức add thay vì phương thức thay thế của lớp FragmentTransaction sau đó bạn phải thêm secondFragment để xếp chồng bằng phương thức addToBackStack

Thứ hai : khi nhấp lại, bạn phải gọi popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};

0

Thay thế một đoạn bằng mã sau đây:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

Hoạt động của onBackPression () là:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}

0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);

1
Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp hạn chế, ngay lập tức. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
Machavity

0

Giải pháp hoàn hảo tìm thấy đoạn cũ trong ngăn xếp và tải nó nếu tồn tại trong ngăn xếp.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

sử dụng nó như

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.