Để tái sử dụng các phân mảnh, hãy tạo từng phân mảnh dưới dạng một thành phần hoàn toàn độc lập xác định bố cục và hành vi của chính cấu hình đó. Sau khi đã xác định được các phân mảnh tái sử dụng này, có thể liên kết các phân mảnh đó với hoạt động và kết nối chúng với logic ứng dụng để nhận dạng giao diện người dùng tổng hợp.
Để phản ứng đúng cách với các sự kiện hoặc chia sẻ thông tin trạng thái người dùng, thông thường cần có các kênh liên lạc giữa một hoạt động và các phân mảnh hoạt động đó, hoặc giữa hai hoặc nhiều phân mảnh. Để giữ phân mảnh riêng biệt, không nên để các phân mảnh giao tiếp trực tiếp với phân mảnh khác hoặc với hoạt động lưu trữ.
Thư viện Fragment
cung cấp hai tùy chọn giao tiếp: ViewModel
dùng chung và API Kết quả phân mảnh. Tùy chọn được đề xuất phụ thuộc vào trường hợp sử dụng. Để chia sẻ dữ liệu ổn định với bất kỳ API tùy chỉnh nào, bạn nên sử dụng ViewModel
. Để có kết quả một lần có dữ liệu có thể được đặt trong Bundle
, bạn nên sử dụng API kết quả phân mảnh.
Các phần sau cho biết cách sử dụng ViewModel
và API kết quả phân mảnh để liên lạc giữa các phân mảnh và hoạt động.
Chia sẻ dữ liệu bằng ViewModel
ViewModel
là lựa chọn lý tưởng khi cần chia sẻ dữ liệu giữa nhiều phân mảnh hoặc giữa các phân mảnh và hoạt động của máy chủ.
Các đối tượng ViewModel
lưu trữ và quản lý dữ liệu giao diện người dùng. Để biết thêm thông tin về ViewModel
, vui lòng xem nội dungtổng quan về ViewModel.
Chia sẻ dữ liệu với hoạt động của máy chủ lưu trữ
Trong một số trường hợp, bạn cần chia sẻ dữ liệu giữa các phân mảnh và hoạt động của máy chủ lưu trữ. Ví dụ như khi bạn muốn chuyển đổi một thành phần giao diện người dùng toàn cầu dựa trên tương tác trong một phân mảnh.
Hãy cân nhắc những tính năng sau ItemViewModel
:
Kotlin
class ItemViewModel : ViewModel() { private val mutableSelectedItem = MutableLiveData<Item>() val selectedItem: LiveData<Item> get() = mutableSelectedItem fun selectItem(item: Item) { mutableSelectedItem.value = item } }
Java
public class ItemViewModel extends ViewModel { private final MutableLiveData<Item> selectedItem = new MutableLiveData<Item>(); public void selectItem(Item item) { selectedItem.setValue(item); } public LiveData<Item> getSelectedItem() { return selectedItem; } }
Trong ví dụ này, dữ liệu đang lưu trữ được gói trong lớp MutableLiveData
.
LiveData
là một lớp lưu giữ dữ liệu có thể quan sát và nhận biết được trong vòng đời. MutableLiveData
cho phép thay đổi giá trị của thuộc tính này. Để biết thêm thông tin về LiveData
, vui lòng xem nội dung tổng quan về DataData.
Cả phân mảnh và hoạt động máy chủ lưu trữ đều có thể truy xuất một bản sao được chia sẻ của ViewModel
có phạm vi hoạt động bằng cách chuyển hoạt động vào hàm tạo ViewModelProvider
. ViewModelProvider
xử lý việc tạo bản sao cho ViewModel
hoặc truy xuất phần tử đó nếu đã tồn tại. Cả hai thành phần đều có thể quan sát và sửa đổi dữ liệu:
Kotlin
class MainActivity : AppCompatActivity() { // Using the viewModels() Kotlin property delegate from the activity-ktx // artifact to retrieve the ViewModel in the activity scope private val viewModel: ItemViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.selectedItem.observe(this, Observer { item -> // Perform an action with the latest item data }) } } class ListFragment : Fragment() { // Using the activityViewModels() Kotlin property delegate from the // fragment-ktx artifact to retrieve the ViewModel in the activity scope private val viewModel: ItemViewModel by activityViewModels() // Called when the item is clicked fun onItemClicked(item: Item) { // Set a new item viewModel.selectItem(item) } }
Java
public class MainActivity extends AppCompatActivity { private ItemViewModel viewModel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewModel = new ViewModelProvider(this).get(ItemViewModel.class); viewModel.getSelectedItem().observe(this, item -> { // Perform an action with the latest item data }); } } public class ListFragment extends Fragment { private ItemViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(ItemViewModel.class); ... items.setOnClickListener(item -> { // Set a new item viewModel.select(item); }); } }
Chia sẻ dữ liệu giữa các phân mảnh
Hai hoặc nhiều phân mảnh trong cùng một hoạt động thường cần giao tiếp với nhau. Ví dụ: hãy tưởng tượng một phân mảnh hiển thị một danh sách và một phân mảnhkhác cho phép người dùng áp dụng nhiều bộ lọc khác nhau cho danh sách. Trường hợp này có thể không hề đơn giản để triển khai nếu không có phân mảnh ghép giao tiếp trực tiếp, nghĩa là các trường hợp đó không còn độc lập nữa. Ngoài ra, cả hai phân mảnh đều phải xử lý trường hợp phân mảnh khác chưa được tạo hoặc hiển thị.
Các phân mảnh này có thể chia sẻ ViewModel
bằng cách sử dụng phạm vi hoạt động để xử lý hoạt động giao tiếp. Bằng cách chia sẻ ViewModel
theo cách đó, các phân mảnh không cần phải nhận biết về nhau cũng như hoạt động không cần làm gì để hỗ trợ cho việc giao tiếp.
Ví dụ sau đây cho thấy cách hai phân mảnh có thể sử dụng ViewModel
dùng chung để giao tiếp:
Kotlin
class ListViewModel : ViewModel() { val filters = MutableLiveData<Set<Filter>>() private val originalList: LiveData<List<Item>>() = ... val filteredList: LiveData<List<Item>> = ... fun addFilter(filter: Filter) { ... } fun removeFilter(filter: Filter) { ... } } class ListFragment : Fragment() { // Using the activityViewModels() Kotlin property delegate from the // fragment-ktx artifact to retrieve the ViewModel in the activity scope private val viewModel: ListViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.filteredList.observe(viewLifecycleOwner, Observer { list -> // Update the list UI } } } class FilterFragment : Fragment() { private val viewModel: ListViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.filters.observe(viewLifecycleOwner, Observer { set -> // Update the selected filters UI } } fun onFilterSelected(filter: Filter) = viewModel.addFilter(filter) fun onFilterDeselected(filter: Filter) = viewModel.removeFilter(filter) }
Java
public class ListViewModel extends ViewModel { private final MutableLiveData<Set<Filter>> filters = new MutableLiveData<>(); private final LiveData<List<Item>> originalList = ...; private final LiveData<List<Item>> filteredList = ...; public LiveData<List<Item>> getFilteredList() { return filteredList; } public LiveData<Set<Filter>> getFilters() { return filters; } public void addFilter(Filter filter) { ... } public void removeFilter(Filter filter) { ... } } public class ListFragment extends Fragment { private ListViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class); viewModel.getFilteredList().observe(getViewLifecycleOwner(), list -> { // Update the list UI }); } } public class FilterFragment extends Fragment { private ListViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class); viewModel.getFilters().observe(getViewLifecycleOwner(), set -> { // Update the selected filters UI }); } public void onFilterSelected(Filter filter) { viewModel.addFilter(filter); } public void onFilterDeselected(Filter filter) { viewModel.removeFilter(filter); } }
Lưu ý cả hai phân mảnh đều sử dụng hoạt động của máy chủ làm phạm vi cho ViewModelProvider
. Vì các phân mảnh sử dụng cùng một phạm vi nên chúng nhận cùng một bản sao của ViewModel
, cho phép giao tiếp qua lại.
Chia sẻ dữ liệu giữa phân mảnh gốc và phân mảnh con
Khi làm việc với các phân mảnh con, phân mảnh gốc và phân mảnh con có thể cần chia sẻ dữ liệu với nhau. Để chia sẻ dữ liệu giữa các phân mảnh, hãy lấy phân mảnh gốc làm phạm vi ViewModel
.
Kotlin
class ListFragment: Fragment() { // Using the viewModels() Kotlin property delegate from the fragment-ktx // artifact to retrieve the ViewModel private val viewModel: ListViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.filteredList.observe(viewLifecycleOwner, Observer { list -> // Update the list UI } } } class ChildFragment: Fragment() { // Using the viewModels() Kotlin property delegate from the fragment-ktx // artifact to retrieve the ViewModel using the parent fragment's scope private val viewModel: ListViewModel by viewModels({requireParentFragment()}) ... }
Java
public class ListFragment extends Fragment { private ListViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { viewModel = new ViewModelProvider(this).get(ListViewModel.class); viewModel.getFilteredList().observe(getViewLifecycleOwner(), list -> { // Update the list UI } } } public class ChildFragment extends Fragment { private ListViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { viewModel = new ViewModelProvider(requireParentFragment()).get(ListViewModel.class); ... } }
Đưa ViewModel
vào Biểu đồ điều hướng
Nếu đang sử dụng Thư viện điều hướng, bạn cũng có thể phân ViewModel
cho vòng đời của đích đến NavBackStackEntry
. Ví dụ: ViewModel
có thể nằm trong phạm vi NavBackStackEntry
của ListFragment
:
Kotlin
class ListFragment: Fragment() { // Using the navGraphViewModels() Kotlin property delegate from the fragment-ktx // artifact to retrieve the ViewModel using the NavBackStackEntry scope // R.id.list_fragment == the destination id of the ListFragment destination private val viewModel: ListViewModel by navGraphViewModels(R.id.list_fragment) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.filteredList.observe(viewLifecycleOwner, Observer { item -> // Update the list UI } } }
Java
public class ListFragment extends Fragment { private ListViewModel viewModel; @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.list_fragment) viewModel = new ViewModelProvider(backStackEntry).get(ListViewModel.class); viewModel.getFilteredList().observe(getViewLifecycleOwner(), list -> { // Update the list UI } } }
Để biết thêm thông tin về việc phạm vi ViewModel
với một NavBackStackEntry
, hãy xem nội dung Tương tác có lập trình với thành phần Điều hướng.
Nhận kết quả bằng cách sử dụng API kết quả phân mảnh
Trong một số trường hợp, bạn có thể muốn chuyển giá trị một lần giữa hai phân mảnh hoặc giữa một phân mảnh với hoạt động của máy chủ. Ví dụ: bạn có một phân mảnh với mã đọc QR, chuyển dữ liệu trở lại phân mảnh trước.
Bắt đầu bằng phân mảnh 1.3.0-alpha04, mỗi FragmentManager
triển khai FragmentResultOwner
.
Điều này có nghĩa là FragmentManager
hoạt động như một kho lưu trữ trung tâm cho các kết quả phân mảnh. Thay đổi này cho phép các thành phần giao tiếp với nhau bằng cách đặt kết quả phân mảnh và lắng nghe kết quả đó mà không yêu cầu các thành phần tham chiếu trực tiếp với nhau.
Chuyển kết quả giữa các phân mảnh
Để chuyển dữ liệu từ phân mảnh B trở về phân mảnh A, trước tiên hãy đặt trình nghe xử lý kết quả trên phân mảnh A là phân mảnh nhận kết quả. Gọi hàm setFragmentResultListener()
trên FragmentManager
của phân mảnh A như trong ví dụ sau:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Use the Kotlin extension in the fragment-ktx artifact setFragmentResultListener("requestKey") { requestKey, bundle -> // We use a String here, but any type that can be put in a Bundle is supported val result = bundle.getString("bundleKey") // Do something with the result } }
Java
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getParentFragmentManager().setFragmentResultListener("requestKey", this, new FragmentResultListener() { @Override public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle bundle) { // We use a String here, but any type that can be put in a Bundle is supported String result = bundle.getString("bundleKey"); // Do something with the result } }); }

FragmentManager
.Trong phân mảnh B (là phân mảnh tạo ra kết quả), bạn phải đặt kết quả trên cùng FragmentManager
bằng cách sử dụng cùng một requestKey
. Có thể thực hiện việc này bằng cách sử dụng APIsetFragmentResult()
:
Kotlin
button.setOnClickListener { val result = "result" // Use the Kotlin extension in the fragment-ktx artifact setFragmentResult("requestKey", bundleOf("bundleKey" to result)) }
Java
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Bundle result = new Bundle(); result.putString("bundleKey", "result"); getParentFragmentManager().setFragmentResult("requestKey", result); } });
Sau đó, phân mảnh A sẽ nhận được kết quả và thực thi lệnh gọi lại trình xử lý nếu phân mảnh làSTARTED
.
Bạn chỉ có thể sở hữu một trình nghe và kết quả cho một khóa nhất định. Nếu gọi setFragmentResult()
nhiều lần cho cùng một khóa và nếu trình xử lý không phải là STARTED
, hệ thống sẽ thay thế mọi kết quả đang chờ xử lý bằng kết quả đã cập nhật. Nếu đặt một kết quả nhưng không có trình nghe tương ứng để nhận kết quả, kết quả sẽ được lưu trữ trong FragmentManager
cho đến khi bạn đặt một trình nghe bằng khóa tương tự. Khi người nghe nhận được kết quả và kích hoạt lệnh gọi lại onFragmentResult()
, kết quả sẽ bị xóa. Hành vi này có hai ảnh hưởng quan trọng:
- Các phân mảnh trong ngăn xếp lui không nhận được kết quả cho đến khi xuất hiện và là phân mảnh
STARTED
. - Nếu một phân mảnh đang nghe kết quả là
STARTED
khi kết quả được đặt, lệnh gọi lại của trình nghe sẽ được kích hoạt ngay lập tức.
Kiểm tra kết quả phân mảnh
Sử dụng FragmentScenario
để thử nghiệm các cuộc gọi tới setFragmentResult()
và setFragmentResultListener()
.
Tạo tình huống cho đoạn mã đang được kiểm tra bằng cách sử dụng launchFragmentInContainer
hoặc launchFragment
, sau đó gọi phương thức theo cách thủ công chưa được thử nghiệm.
Để kiểm tra setFragmentResultListener()
, hãy tạo một tình huống với phân mảnh thực hiện lệnh gọi đến setFragmentResultListener()
. Tiếp theo, hãy gọi trực tiếp cho setFragmentResult()
và xác minh kết quả:
@Test
fun testFragmentResultListener() {
val scenario = launchFragmentInContainer<ResultListenerFragment>()
scenario.onFragment { fragment ->
val expectedResult = "result"
fragment.parentFragmentManager.setFragmentResult("requestKey", bundleOf("bundleKey" to expectedResult))
assertThat(fragment.result).isEqualTo(expectedResult)
}
}
class ResultListenerFragment : Fragment() {
var result : String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, bundle ->
result = bundle.getString("bundleKey")
}
}
}
Để kiểm tra setFragmentResult()
, hãy tạo một tình huống có đoạn thực hiện lệnh gọi đến setFragmentResult()
. Tiếp theo, hãy gọi trực tiếp setFragmentResultListener()
và xác minh kết quả:
@Test
fun testFragmentResult() {
val scenario = launchFragmentInContainer<ResultFragment>()
lateinit var actualResult: String?
scenario.onFragment { fragment ->
fragment.parentFragmentManager
.setFragmentResultListener("requestKey") { requestKey, bundle ->
actualResult = bundle.getString("bundleKey")
}
}
onView(withId(R.id.result_button)).perform(click())
assertThat(actualResult).isEqualTo("result")
}
class ResultFragment : Fragment(R.layout.fragment_result) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById(R.id.result_button).setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
}
}
Chuyển kết quả giữa phân mảnh gốc và phân mảnh con
Để chuyển kết quả từ một phân mảnh con sang phân mảnh gốc, phân mảnh gốc phải sử dụng getChildFragmentManager()
thay vì getParentFragmentManager()
khi gọi setFragmentResultListener()
.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // We set the listener on the child fragmentManager childFragmentManager.setFragmentResultListener("requestKey") { key, bundle -> val result = bundle.getString("bundleKey") // Do something with the result } }
Java
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // We set the listener on the child fragmentManager getChildFragmentManager() .setFragmentResultListener("requestKey", this, new FragmentResultListener() { @Override public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle bundle) { String result = bundle.getString("bundleKey"); // Do something with the result } }); }

FragmentManager
để gửi kết quả đến phân mảnh gốc.phân mảnh con đặt kết quả trên FragmentManager
. Sau đó, phân mảnh gốc sẽ nhận được kết quả khi nó là phân mảnh STARTED
:
Kotlin
button.setOnClickListener { val result = "result" // Use the Kotlin extension in the fragment-ktx artifact setFragmentResult("requestKey", bundleOf("bundleKey" to result)) }
Java
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Bundle result = new Bundle(); result.putString("bundleKey", "result"); // The child fragment needs to still set the result on its parent fragment manager getParentFragmentManager().setFragmentResult("requestKey", result); } });
Nhận kết quả trong hoạt động trên máy chủ lưu trữ
Để nhận được kết quả phân mảnh trong hoạt động lưu trữ, hãy đặt trình xử lý kết quả trên trình quản lý phân mảnh bằng cách sử dụng getSupportFragmentManager()
.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) supportFragmentManager .setFragmentResultListener("requestKey", this) { requestKey, bundle -> // We use a String here, but any type that can be put in a Bundle is supported val result = bundle.getString("bundleKey") // Do something with the result } } }
Java
class MainActivity extends AppCompatActivity { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportFragmentManager().setFragmentResultListener("requestKey", this, new FragmentResultListener() { @Override public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle bundle) { // We use a String here, but any type that can be put in a Bundle is supported String result = bundle.getString("bundleKey"); // Do something with the result } }); } }