web-dev-qa-db-fra.com

Fragments détruits / recréés avec les composants de navigation de Jetpack Android

J'essaie d'implémenter Navigation avec les composants d'architecture de Jetpack dans mon application existante.

J'ai une seule application d'activité où le fragment principal (ListFragment) est une liste d'éléments. Actuellement, lorsque l'utilisateur appuie sur un élément de la liste, un deuxième fragment est ajouté à la pile par fragmentTransaction.add(R.id.main, detailFragment). Ainsi, lorsque vous appuyez sur Retour, le DetailFragment est détaché et le ListFragment s'affiche à nouveau.

Avec l'architecture de navigation, cela est géré automatiquement. Au lieu d'ajouter le nouveau fragment, c'est remplacé , donc la vue du fragment est détruite, onDestroyView() est appelée et onCreateView() est appelée lorsque back est pressée pour recréer la vue .

Je comprends que c'est un bon modèle utilisé avec LiveData et ViewModel pour éviter d'utiliser plus de mémoire que nécessaire, mais dans mon cas, c'est ennuyeux car la liste a une disposition complexe et le gonfler prend du temps et consomme beaucoup de CPU, également parce que je devrai enregistrer la position de défilement de la liste et faire défiler à nouveau vers la même position que l'utilisateur a laissé le fragment. C'est possible mais semble qu'il devrait exister une meilleure façon.

J'ai essayé de "sauvegarder" la vue dans un champ privé sur fragment et de la réutiliser sur onCreateView() si elle est déjà là, mais cela semble un anti-modèle.

private View view = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    if (view == null) {
        view = inflater.inflate(R.layout.fragment_list, container, false);
        //...
    }

    return view;
}

Existe-t-il un autre moyen, plus élégant, d'éviter de regonfler la disposition?

39
pauminku

Ian Lake de google m'a répondu que nous pouvons stocker la vue dans une variable et au lieu de gonfler une nouvelle disposition, il suffit de renvoyer l'instance de vue pré-enregistrée sur onCreateView()

Source: https://Twitter.com/ianhlake/status/1103522856535638016

Leakcanary peut montrer cela comme une fuite mais son faux positif ..

27
erluxman

Vous pouvez avoir une vue persistante pour votre fragment via l'implémentation ci-dessous

BaseFragment

open class BaseFragment : Fragment(){

        var hasInitializedRootView = false
        private var rootView: View? = null

        fun getPersistentView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?, layout: Int): View? {
            if (rootView == null) {
                // Inflate the layout for this fragment
                rootView = inflater?.inflate(layout,container,false)
            } 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).
                (rootView?.getParent() as? ViewGroup)?.removeView(rootView)
            }

            return rootView
        }
    }

MainFragment

class MainFragment : BaseFragment() {


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return getPersistentView(inflater, container, savedInstanceState, R.layout.content_main)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (!hasInitializedRootView) {
            hasInitializedRootView = true
            setListeners()
            loadViews()
        }
    }
}

Source

5
Shahab Rauf

J'ai essayé comme ça, et ça marche pour moi.

  • Init ViewModel by navGraphViewModels (Live on Navigation scope)
  • Stockez tout état à restaurer dans ViewModel
// fragment.kt
private val vm by navGraphViewModels<VM>(R.id.nav_graph) { vmFactory }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // Restore state
    vm.state?.let {
        (recycler.layoutManager as GridLayoutManager).onRestoreInstanceState(it)
    }
}

override fun onPause() {
    super.onPause()
    // Store state
    vm.state = (recycler.layoutManager as GridLayoutManager).onSaveInstanceState()
}

// vm.kt
var state:Parcelable? = null
4
Samnang CHEA

il suffit de mettre une destination personnelle.

<action
   Android:id="@+id/action_piecesReferenceCount_self"
   app:destination="@id/piecesReferenceCount" />
Navigation.findNavController(myview).navigate(R.id.action_piecesReferenceCount_self);

0