Parçalarla iletişim kurma

Parçaları yeniden kullanmak için bunları tamamen bağımsız bileşenler olarak derleyin birçok farklı reklam bulunur. Bu görevleri tanımladıktan sonra bunları bir etkinlikle ilişkilendirip bunları bir etkinlikte uygulama mantığıyla birleştirdiğinizi düşünün.

Kullanıcı etkinliklerine uygun şekilde tepki vermek ve durum bilgilerini paylaşmak için genellikle Bir etkinlik ile onun etkinliği arasında bir iletişim kanalı olması parça veya iki ya da daha fazla parça arasında geçiş yapar. Parçaların bağımsız olmaları için parçaların doğrudan diğer parçalarla iletişim kurmasını sağlamayın veya etkinliğiyle eşleştirilir.

Fragment kitaplığı iki iletişim seçeneği sunar: paylaşılan ViewModel ve Parça Result API'si. Önerilen seçenek, kullanım alanına bağlıdır. Paylaşmak için özel API'lerle kalıcı veriler sağlamak için ViewModel kullanın. Örneğin, verileri içeren tek seferlik bir sonuç Bundle, Parçayı kullanın Result API'si.

Aşağıdaki bölümlerde ViewModel ve Parçanın nasıl kullanılacağı gösterilmektedir Parçalarınız ve etkinlikleriniz arasında iletişim kurmak için Result API'si.

ViewModel kullanarak veri paylaşma

Aşağıdaki durumlarda ViewModel ideal seçenektir: ortak bir aktiviteye dahil olmak için parçalar ve ana makine etkinlikleri arasında geçiş yapar. ViewModel nesne depolanır ve kullanıcı arayüzü verilerini yönetmenizi sağlar. ViewModel hakkında daha fazla bilgi için bkz. ViewModel'e genel bakış.

Verileri, düzenleyen kişiyle paylaşma

Bazı durumlarda, parçalar ve davet eder. Örneğin, genel bir kullanıcı arayüzünü açıp kapatmak isteyebilirsiniz. parça içindeki etkileşimi temel alır.

Aşağıdaki ItemViewModel göz önünde bulundurulmalıdır:

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

Bu örnekte, depolanan veriler MutableLiveData sınıfı. LiveData, yaşam döngüsüne duyarlı bir gözlemlenebilir veri sahibi sınıfını kullanır. MutableLiveData, değerinin değiştirildi. LiveData hakkında daha fazla bilgi için bkz. LiveData'ya genel bakış.

Hem parçanız hem de parçanızın ana makine etkinliği, paylaşılan bir örneği alabilir etkinliğiViewModel ViewModelProvider kurucusu. ViewModelProvider, ViewModel örneklendirmeyi işler dosya zaten varsa alın. Her iki bileşen de bu verileri değiştirebilir.

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

Parçalar arasında veri paylaşma

Aynı etkinlikteki iki veya daha fazla parçanın genellikle bir iletişim kurmaktır. Mesela bir parçanın bir listede yer aldığını Kullanıcının listeye çeşitli filtreler uygulamasına olanak tanıyan başka bir filtre de oluşturabilirsiniz. Parçalar olmadan bu senaryoyu uygulamak önemsiz değil. ancak doğrudan iletişim kurmayıp bağımsız olması gerekir. Ayrıca, her iki parçanın da senaryoya uygun olması gerekir. olması gerekir.

Bu parçalar, etkinlik kapsamlarını kullanarak bir ViewModel paylaşabilir çok iyi bir fikir olabilir. ViewModel bu şekilde paylaşıldığında, parçaların birbirlerinden haberdar olmaları gerekmez ve etkinlik ve iletişimi kolaylaştırmak için bir şey yapması gerekmez.

Aşağıdaki örnekte, iki parçanın paylaşılan bir öğeyi nasıl kullanabileceği gösterilmektedir İletişim için ViewModel:

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

Her iki parça da ana makine etkinliğini ViewModelProvider Parçalar aynı kapsamı kullandığından iletişim kurmalarına olanak tanıyan aynı ViewModel örneği iletişim kurabiliyorsunuz.

Üst ve alt parça arasında veri paylaşma

Alt parçalarla çalışırken üst parçanız ve alt parçanız parçaların birbirleriyle veri paylaşması gerekebilir. Şu hizmetler arasında veri paylaşmak için: için, üst parçayı gösterildiği gibi ViewModel kapsamı olarak kullanın aşağıdaki örnekte:

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

Bir ViewModel'in Kapsamını Gezinme Grafiği'ne ekleme

Navigasyon kitaplığını kullanıyorsanız şunları da bulabilirsiniz: bir ViewModel kapsamını bir hedefin yaşam döngüsüne NavBackStackEntry. Örneğin, örnek olarak, ViewModel metriği NavBackStackEntry olarak ayarlanabilir. ListFragment için:

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.
        }
    }
}

Bir ViewModel kapsamının NavBackStackEntry olarak belirlenmesi hakkında daha fazla bilgi için bkz. Gezinme bileşeniyle programlı bir şekilde etkileşim kurun.

Fragment Result API'sini kullanarak sonuç alma

Bazı durumlarda, iki parça arasında tek seferlik bir değer aktarmak isteyebilirsiniz veya parça ile ana makine etkinliği arasında. Örneğin, bir hafta içinde QR kodlarını okuyan ve verileri önceki bir parçaya geri ileten parça.

Parça sürüm 1.3.0 ve sonraki sürümlerde her FragmentManager implements FragmentResultOwner. Bu, bir FragmentManager öğesinin parça için merkezi bir depo olarak kullanabileceği anlamına gelir sonuç. Bu değişiklik, bileşenlerin birbiriyle iletişim kurmasını sağlayan gerek kalmadan parçalı sonuçları ayarlama ve bu sonuçları dinleme bu bileşenlerin birbirine doğrudan referanslar içermesini sağlar.

Sonuçları parçalar arasında iletme

Verileri B parçasından A parçasına geri iletmek için önce bir sonuç işleyici ayarlayın A parçasında, sonucu alan parça olur. Telefonla arama setFragmentResultListener() aşağıdaki örnekte gösterildiği gibi A parçasının FragmentManager üzerinde:

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.
        }
    });
}
b parçası bir FragmentManager kullanarak a parçasına veri gönderir
Şekil 1. B parçası, verileri FragmentManager.

B parçasında, sonucu üreten parça, aynı requestKey kullanarak aynı FragmentManager üzerinde. Yapabilecekleriniz Bu nedenle, setFragmentResult() API:

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

A parçası daha sonra sonucu alır ve dinleyici geri çağırmasını yürütür parça daha sonra STARTED.

Belirli bir anahtar için yalnızca tek bir işleyici ve sonuç olabilir. Arama yaptığınızda setFragmentResult() aynı tuş için birden fazla kez ve dinleyici STARTED değil, sistem bekleyen sonuçları güncel yardımcı olur.

Karşılık gelen bir dinleyici olmadan bir sonucu ayarlarsanız bir işleyici ayarlanana kadar sonuç FragmentManager içinde saklanır aynı tuşa sahiptir. Dinleyici bir sonuç aldıktan sonra onFragmentResult() geri çağırması, sonuç temizlendi. Bu davranış şu iki çıkarıma denk gelir:

  • Arka yığındaki parçalar, oluşturulana kadar patlatıldı ve STARTED.
  • Sonuç ayarlandığında bir sonucu dinleyen parça STARTED ise dinleyicinin geri araması hemen tetiklenir.
ziyaret edin.

Parça sonuçlarını test et

Tekliflerinizi otomatikleştirmek ve optimize etmek için FragmentScenario setFragmentResult() ve setFragmentResultListener() numaralı telefona yapılan aramaları test edin. Aşağıdaki kodu kullanarak test edilen parça için bir senaryo oluşturun: launchFragmentInContainer veya launchFragment, ardından test edilmeyen yöntemi manuel olarak çağırın.

setFragmentResultListener() test etmek için setFragmentResultListener() çağrısını yapan parçası. Sonra, setFragmentResult() adlı kişiyi doğrudan çağırın ve sonucu doğrulayın:

@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")
        }
    }
}

setFragmentResult() öğesini test etmek için setFragmentResult() numaralı telefona sesli arama yapın. Sonra setFragmentResultListener() numaralı telefonu arayın girin ve sonucu doğrulayın:

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

Sonuçları üst ve alt parçalar arasında iletme

Bir alt parçadan elde edilen sonucu üst öğeye iletmek için yerine üst parçadan getChildFragmentManager() kullan setFragmentResultListener() aranıyor getParentFragmentManager().

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // 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);
    // 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.
            }
        });
}
bir alt parça, sonuç göndermek için FragmentManager&#39;ı kullanabilir
            ana yayıncısına
Şekil 2 Bir alt parça, FragmentManager kullanarak üst öğesine sonuç gönderin.

Alt parça, sonucu FragmentManager öğesinde ayarlar. Üst öğe ve parça STARTED olduğunda sonucu alır:

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

Düzenleyen etkinliğiyle ilgili sonuçları al

Ana makine etkinliğinde bir parça sonucu almak için sonuç işleyici ayarlayın parça yöneticisinde 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.
            }
        });
    }
}