Parçalarla iletişim kurma

Parçaları yeniden kullanmak için, bunları kendi düzen ve davranışlarını tanımlayan, tamamen bağımsız bileşenler olarak oluşturun. Yeniden kullanılabilir bu parçaları tanımladıktan sonra, bunları bir etkinlikle ilişkilendirebilir ve genel birleşik kullanıcı arayüzünü elde etmek için bunları uygulama mantığına bağlayabilirsiniz.

Kullanıcı etkinliklerine doğru şekilde tepki vermek ve durum bilgilerini paylaşmak için genellikle bir etkinlik ile onun parçaları ya da iki veya daha fazla parça arasında iletişim kanalları olması gerekir. Parçaların bağımsız kalması için doğrudan diğer parçalarla veya ana makine etkinlikleriyle iletişim kuran parçalar kullanmayın.

Fragment kitaplığı iki iletişim seçeneği sunar: paylaşılan ViewModel ve Fragment Result API. Önerilen seçenek, kullanım alanına bağlıdır. Kalıcı verileri özel API'lerle paylaşmak için ViewModel kullanın. Bundle içine yerleştirilebilen verilerle tek seferlik bir sonuç almak için FragmentResult API'yi kullanın.

Aşağıdaki bölümlerde, parçalarınız ve etkinlikleriniz arasında iletişim kurmak için ViewModel ve FragmentResult API'yi nasıl kullanacağınız gösterilmektedir.

ViewModel kullanarak verileri paylaşma

ViewModel, birden fazla parça arasında veya parçalar ile bunların ana makine etkinlikleri arasında veri paylaşmanız gerektiğinde ideal bir seçenektir. ViewModel nesneleri, kullanıcı arayüzü verilerini depolar ve yönetir. ViewModel hakkında daha fazla bilgi için ViewModel'e genel bakış bölümüne bakın.

Düzenleyen etkinliğiyle veri paylaşma

Bazı durumlarda, parçalar ve bunların ana makine etkinlikleri arasında veri paylaşmanız gerekebilir. Örneğin, global kullanıcı arayüzü bileşenini parça içindeki etkileşime dayalı olarak değiştirmek isteyebilirsiniz.

Şunları göz önünde bulundurun: 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;
    }
}

Bu örnekte, depolanan veriler bir MutableLiveData sınıfı içine alınmıştır. LiveData, yaşam döngüsüne duyarlı bir gözlemlenebilir veri sahibi sınıfıdır. MutableLiveData, değerinin değiştirilmesine izin verir. LiveData hakkında daha fazla bilgi için LiveData'ya genel bakış sayfasını inceleyin.

Hem parçanız hem de ana makine etkinliği, etkinliği ViewModelProvider oluşturucuya ileterek etkinlik kapsamına sahip bir ViewModel öğesinin paylaşılan örneğini alabilir. ViewModelProvider, ViewModel örneğini veya varsa geri almayı yönetir. Her iki bileşen de bu verileri gözlemleyip 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 birbiriyle iletişim kurması gerekir. Örneğin, bir liste görüntüleyen bir parça ve kullanıcının listeye çeşitli filtreler uygulamasına olanak tanıyan başka bir parça düşünün. Parçalar doğrudan bir şekilde iletişim kurmadan bu örneği uygulamak önemsiz olmayacak, ancak daha sonra artık bağımsız olmaz. Buna ek olarak, her iki parça da diğer parçanın henüz oluşturulmadığı veya görünür olmadığı senaryoyu işlemelidir.

Bu parçalar, bu iletişimi yönetmek için etkinlik kapsamlarını kullanarak bir ViewModel öğesini paylaşabilir. ViewModel bu şekilde paylaşıldığında parçaların birbirlerini bilmesi gerekmez ve etkinliğin iletişimi kolaylaştırmak için herhangi bir şey yapmasına gerek yoktur.

Aşağıdaki örnekte iki parçanın iletişim kurmak için paylaşılan bir ViewModel'yi nasıl kullanabileceği gösterilmektedir:

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 ViewModelProvider için kapsam olarak ana makine etkinliğini kullanır. Parçalar aynı kapsamı kullandığından aynı ViewModel örneğini alırlar. Bu da iki parçanın birbiriyle iletişim kurmasını sağlar.

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

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

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

ViewModel'i Gezinme Grafiğine Alma

Gezinme kitaplığı kullanıyorsanız bir hedefin NavBackStackEntry yaşam döngüsüne ViewModel de dahil edebilirsiniz. Örneğin, ViewModel, ListFragment için NavBackStackEntry olarak ayarlanabilir:

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 için NavBackStackEntry kapsamını belirleme hakkında daha fazla bilgi edinmek istiyorsanız Gezinme bileşeniyle programatik olarak etkileşime geçme bölümüne bakın.

Fragment Result API'yi kullanarak sonuç alma

Bazı durumlarda, iki parça veya bir parça ile ana makine etkinliği arasında tek seferlik bir değer geçirmek isteyebilirsiniz. Örneğin, QR kodlarını okuyan ve verileri tekrar önceki bir parçaya ileten bir parçanız olabilir.

Fragment 1.3.0 ve sonraki sürümlerde her FragmentManager FragmentResultOwner kodunu uygular. Bu, FragmentManager öğesinin parça sonuçları için merkezi bir depolama alanı görevi görebileceği anlamına gelir. Bu değişiklik, bileşenlerin birbirine doğrudan referansları olmasını gerektirmeden, parça sonuçlarını ayarlayıp bu sonuçları dinleyerek birbirleriyle iletişim kurmasına olanak tanır.

Sonuçları parçalar arasında geçirme

Verileri B parçasından A parçasına geri aktarmak için önce sonucu alan parça olan A parçasında bir sonuç işleyici ayarlayın. Aşağıdaki örnekte gösterildiği gibi A parçasının FragmentManager öğesinde setFragmentResultListener() çağrısı yapın:

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ı, bir FragmentManager kullanarak A parçasına veri gönderir.

B parçasında, sonucu üreten parça, aynı requestKey öğesini kullanarak sonucu aynı FragmentManager ile ayarlar. Bunu setFragmentResult() API'sini kullanarak yapabilirsiniz:

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

Daha sonra A parçası, sonucu alır ve parça STARTED olduğunda işleyici geri çağırma işlemini yürütür.

Belirli bir anahtar için yalnızca tek bir işleyiciniz ve sonucunuz olabilir. Aynı anahtar için setFragmentResult() öğesini birden fazla kez çağırırsanız ve işleyici STARTED değilse sistem bekleyen sonuçları güncellenmiş sonucunuzla değiştirir.

Bir sonucu almak için karşılık gelen bir işleyici olmadan ayarlarsanız sonuç, aynı anahtarla bir işleyici ayarlanana kadar FragmentManager içinde depolanır. İşleyici bir sonuç alıp onFragmentResult() geri çağırmasını tetiklediğinde sonuç temizlenir. Bu davranışın iki ana sonucu vardır:

  • Arka yığındaki parçalar, açılıp STARTED olana kadar sonuç almaz.
  • Sonuç ayarlanırken bir sonuç için dinleme parçası STARTED olursa dinleyicinin geri çağırması hemen etkinleşir.

Test parçası sonuçları

setFragmentResult() ve setFragmentResultListener() çağrılarını test etmek için FragmentScenario aracını kullanın. launchFragmentInContainer veya launchFragment kullanarak test edilen parça için bir senaryo oluşturun, ardından test edilmeyen yöntemi manuel olarak çağırın.

setFragmentResultListener() öğesini test etmek için setFragmentResultListener() çağrısını yapan parçayla bir senaryo oluşturun. Ardından, doğrudan setFragmentResult() numaralı telefonu arayı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() çağrısını yapan parçayla bir senaryo oluşturun. Daha sonra, doğrudan setFragmentResultListener() telefonunu arayın 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 geçirme

Bir alt parçadaki sonucu üst parçaya aktarmak için setFragmentResultListener() yöntemini çağırırken getParentFragmentManager() yerine üst parçadan getChildFragmentManager() kullanın.

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, sonucu üst öğesine göndermek için FragmentManager&#39;ı kullanabilir
Şekil 2 Bir alt parça, sonucu üst öğesine göndermek için FragmentManager kullanabilir.

Alt parça, sonucu FragmentManager öğesine ayarlar. Üst öğe, 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);
    }
});

Ana makine etkinliğindeki sonuçları alma

Ana makine etkinliğinde bir parça sonucu almak için parça yöneticisinde getSupportFragmentManager() kodunu kullanarak bir sonuç işleyici ayarlayın.

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