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?
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 ..
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()
}
}
}
J'ai essayé comme ça, et ça marche pour moi.
ViewModel
by navGraphViewModels
(Live on Navigation scope)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
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);