Comment utiliser les extensions Android Kotlin avec Fragment
s? Si je les utilise dans onCreateView()
, j'obtiens cette exception NullPointerException
:
Causée par: Java.lang.NullPointerException: tente d'appeler virtual méthode 'Android.view.View Android.view.View.findViewById (int)' sur un référence d'objet null
Voici le code de fragment:
package com.obaied.testrun.Fragment
import Android.os.Bundle
import Android.support.v4.app.Fragment
import Android.util.Log
import Android.view.LayoutInflater
import Android.view.View
import Android.view.ViewGroup
import com.obaied.acaan.R
import kotlinx.Android.synthetic.main.fragment_card_selector.*
public class CardSelectorFragment : Fragment() {
val TAG = javaClass.canonicalName
companion object {
fun newInstance(): CardSelectorFragment {
return CardSelectorFragment()
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
return rootView
}
}
`
Les propriétés synthétiques de Kotlin ne sont pas magiques et fonctionnent de manière très simple. Lorsque vous accédez à btn_K
, il appelle getView().findViewById(R.id.btn_K)
.
Le problème est que vous y accédez trop tôt. getView()
renvoie null
en onCreateView
. Essayez de le faire avec la méthode onViewCreated
:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}
Vous appelez ce btn_K
trop tôt car à ce moment-là, il renvoie une valeur null et vous donne une exception de pointeur nul.
Vous pouvez utiliser ces vues avec ce plugin synthétique dans la méthode onActivityCreated()
qui est appelée juste après onCreateView()
du cycle de vie du fragment.
onActivityCreated()
{
super.onActivityCreated(savedInstanceState)
btn_K.setOnClickListener{}
}
la seule chose à faire est de:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
rootView.btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
return rootView
}
pas besoin de définir un objet compagnon, il suffit d'appeler chaque identifiant par une vue comme
lateinit var mView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mView=inflater.inflate(R.layout.product_list,container,false)
mView.addProduct.setOnClickListener {
val intent=Intent(activity,ProductAddActivity::class.Java)
startActivity(intent)
} return mView
}
Les propriétés synthétiques générées par le plug-in Kotlin Android Extensions nécessitent la définition d'une view
pour Fragment/Activity
.
Dans votre cas, pour Fragment
, vous devez utiliser view.btn_K
dans onViewCreated
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val view = inflater.inflate(R.layout.fragment_card_selector, container, false)
view.btn_K.setOnClickListener{} // access with `view`
return view
}
Ou mieux, vous ne devriez accéder aux propriétés synthétiques que dans onViewCreated
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_card_selector, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_K.setOnClickListener{} // access without `view`
}
Veuillez noter que le paramètre savedInstanceState
devrait avoir la valeur nullable Bundle?
et également cocher Importer des propriétés synthétiques
Il est pratique d’importer toutes les propriétés du widget pour une présentation spécifique en une fois:
import kotlinx.Android.synthetic.main.<layout>.*
Ainsi, si le fichier de mise en page est activity_main.xml, nous importerions
kotlinx.Android.synthetic.main.activity_main.*.
Si nous voulons appeler les propriétés synthétiques sur View, nous devrions également importer
kotlinx.Android.synthetic.main.activity_main.view.*.
Dans Fragments, veuillez écrire votre code dans onActivityCreated: -
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.login_activity, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
callbackManager = CallbackManager.Factory.create()
initialization()
onClickLogin()
onClickForgot()
onClickSocailLogIn()
}
Les propriétés synthétiques de Kotlin ne sont pas magiques et fonctionnent de manière très simple. Lorsque vous accédez à btn_K, il appelle getView().findViewById(R.id.btn_K)
.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (mRootView == null) {
mRootView = inflater.inflate(R.layout.fragment_profile, container, false)
}
return mRootView
}
soo, si vous utilisez des rappels d’interface, vous obtiendrez NPE car la vue ne sera pas disponible pour le moment -> vous devez remplacer la méthode getView()
override fun getView(): View? {
return mRootView
}
et utilisez toujours vos vues dans onViewCreated()
if (mCreatedView == null) {
mCreatedView = view
btn_K.setOnClickListener{
//------------do something
}
}
Dans mon cas, rien n'a fonctionné jusqu'à ce que j'ai suivi les conseils de Otziii dans les commentaires. Nettoyer, reconstruire (pas de redémarrage nécessaire), réexécuter l'application. Je n'avais pas non plus besoin d'aller avec onActivityCreated
et juste onCreateView
a fait l'affaire.
Une fois, j’ai également commis l’erreur de gonfler une mauvaise mise en page et de ne pas obtenir les contrôles attendus de toute évidence.
En l'ajoutant à la réponse de @Egor Neliuba, Oui, chaque fois que vous appelez une vue sans référence, kotlinex recherche un rootView et, puisque vous êtes à l'intérieur d'un fragment, ce dernier n'a pas de méthode getView()
. Par conséquent, il pourrait lancer NullPointerException
Il y a deux façons de surmonter cela,
onViewCreated()
comme mentionnéOu si vous souhaitez lier des vues dans une autre classe (par exemple, anonyme), vous pouvez simplement créer une fonction d'extension comme celle-ci,
fun View.bindViews(){...}
La deuxième approche est utile lorsque vous avez un seul fragment avec plusieurs comportements.