Outils pour utilisateurs

Outils du site


lang:android:fragment

Communication

Communiquer avec des fragments Archive du 30/05/2023 le 22/08/2023

Le respect des bonnes pratiques de communication devrait permettre de ne pas avoir besoin d'utiliser runOnUiThread(…).

Événements

FragmentResult

Communication en temps réel.

On stocke dans le FragmentManager une action a effectuée quand une clé est appelée.

Puis on génère une événement avec cette clé avec éventuellement des données associées (ou un Bundle vide à défaut).

  • Génération de l'événement :
getParentFragmentManager().setFragmentResult("key", new Bundle())
  • Observateur :
getSupportFragmentManager().setFragmentResultListener("key", this, (requestKey, result) -> {...});

On utilise getSupportFragmentManager() depuis une activity et getParentFragmentManager depuis un fragment.

Il est aussi possible d'appeler clearFragmentResultListener depuis l'action de setFragmentResultListener si on souhaite, par exemple, que le listener n'écoute que le premier appel.

The right way to get a result. Part 2. Fragment Result API Archive du 31/05/2021 le 22/08/2023

LiveData

Communication en temps réel.

LiveData est un champ qui peut être observé. Il s'implémente sur la base d'une classe ViewModel.

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

Ici, Item est soit une classe contenant une ou plusieurs données, soit la version classe d'un type primitif (Boolean, Float, …).

Les données LiveData sont perdues à la fermeture de l'application.

Si un observateur commence à surveiller un LiveData et qu'une valeur a déjà été assignée, la callback est immédiatement appelée (depuis le thread de l'UI).

Un LiveData est commun à tous les éléments ayant le même cycle de vue du constructeur.

  • Observateur

Depuis une activity, on utilise this avec ViewModelProvider car tous les fragments vont avoir la même Activity.

Depuis un fragment, il faut utiliser requireActivity() avec ViewModelProvider.

Pour observe, il faut utiliser this avec onCreate et getViewLifecycleOwner() avec onViewCreated et onActivityCreated. LiveData observing in Fragment Archive du 17/07/2019 le 23/08/2023 The activity lifecycle#oncreate Archive du 24/04/2023 le 23/08/2023 Fragment#getViewLifecycleOwner Archive du 11/08/2023 le 23/08/2023

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

Ici, depuis un fragment, on utilise requireActivity pour les mêmes raisons que ci-dessus.

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
  super.onViewCreated(view, savedInstanceState);
  viewModel = new ViewModelProvider(requireActivity()).get(ItemViewModel.class);
  ...
  viewModel.selectItem(item);
}
  • Notes

LiveData (avec ses méthodes observe, setValue) s'utilise depuis le thread de l'UI. Sinon, il existe postValue si on est en dehors du thread de l'UI.

Il n'est pas possible de modifier ou d'observer uniquement un champ d'un item. Si c'est nécessaire, il faut passer par 2 variables LiveData différentes.

Si un LiveData est modifié 2 fois avant qu'un observateur soit notifié, l'observateur ne verra que la dernière valeur.

Il n'est pas possible d'enlever une valeur à un LiveData. Donc si un observateur observe un LiveData qui n'a jamais été écrit, aucun événement instantané ne sera généré. Mais si une valeur a déjà été assignée, un événement instantané sera systématiquement généré.

If LiveData already has data set, it will be delivered to the observerLiveData Archive du 11/08/2023 le 23/08/2023

setArguments

Passage de l'information avant le chargement du fragment.

Depuis l'activity ou le fragment parent :

final Bundle bundle = new Bundle();
bundle.putBoolean("param", true);
fragment.setArguments(bundle);

Depuis le fragment enfant :

requireArguments().getBoolean("param");

Bouton back

Appel manuel

mainActivity.getSupportFragmentManager().popBackStack();

Changement de fragment

Le comportement du bouton back se définit dans le FragmentManager au moment du chargement du nouveau fragment.

fragmentManager.beginTransaction()...
               .addToBackStack(null)
               .commit();

Puis quand le bouton back sera appelé, les transactions seront inversées jusqu'au précédent appel à addToBackStack.

Evidemment, il est possible de faire plusieurs addToBackStack. Il sera alors possible d'appuyer sur le bouton back autant de fois qu'il y aura eu de addToBackStack. Chaque bouton back remontera d'un cran la pile des transactions.

Depuis onBackPressed event

Dans l'activity, il est possible de surcharger la méthode onBackPressed. Mais cette méthode est dépréciée.

Il faut utiliser :

getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
  @Override
  public void handleOnBackPressed() {
    // Back is pressed... Finishing the activity
    finish();
  }
});

onBackPressed() is deprecated. What is the alternative? Archive du 27/07/2023 le 23/08/2023

Binding

Il existe View binding et Data binding.

Si l'objectif est seulement de remplacer les findViewById, il faut utiliser View binding car plus léger.

Si l'objectif est également de maîtriser les données lors de la reconstruction d'un fragment, Data binding est nécessaire.

View binding

Data binding

  • Chargement du fragment
@Override
@Nullable
public View onCreateView(@NonNull final LayoutInflater inflater,
    @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
  super.onCreateView(inflater, container, savedInstanceState);
 
  binding = NameFragmentBinding.inflate(inflater, container, false);
  binding.setLifecycleOwner(getViewLifecycleOwner());  // Uniquement nécessaire si le binding est lié à un LiveData.
 
  return binding.getRoot();
}

Must I set lifecycleOwner for a data binding in Android Studio? Archive du 10/08/2020 le 23/08/2023

Bind layout views to Architecture Components Archive du 12/07/2023 le 23/08/2023

  • Utilisation des LiveData

Basic Form Validation in Android with Live Data and Data Binding Archive du 07/10/2019 le 23/08/2023

Problèmes

Si une ressource ne génère pas le binding, il faut entourer la ressource par <layout></layout>.

lang/android/fragment.txt · Dernière modification : 2023/08/23 12:15 de root