NullPointerException lors de la tentative d'accès aux vues dans un fragment Kotlin

240

Comment utiliser les extensions Android Kotlin avec Fragments? Si je les utilise à l'intérieur onCreateView(), j'obtiens cette NullPointerExceptionexception:

Causée par: java.lang.NullPointerException: tentative d'appel de la méthode virtuelle 'android.view.View android.view.View.findViewById (int)' sur une référence d'objet null

Voici le code du 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
    }
}
`
solidak
la source
4
Si vous voulez le faire dans onCreateView, btn_K sera également une propriété sur rootView. Vous pourriez fairerootView.btn_K.setOnClickListener
Makotosan
Merci @Makotosan votre réponse a fonctionné pour moi.
Mahdi Bagjani
Nettoyer, reconstruire et redémarrer le studio Android a fonctionné pour moi
Otziii
@Otziii Ce fil a été écrit pour la première fois en 2015. La première réponse a 259 votes et a été acceptée. Je ne pense pas qu'il soit nécessaire d'ajouter plus de réponses.
solidak
2
@Solidak J'ai eu ce problème récemment, j'ai essayé toutes les réponses et la seule chose qui l'a fait fonctionner est ce que j'ai commenté maintenant. J'ai eu une réponse sur ce sujet, mais elle a juste été rejetée, alors je l'ai changé en commentaire. On dirait que les gens ont toujours ce problème, et personne n'a mentionné pour nettoyer et redémarrer.
Otziii

Réponses:

443

Les propriétés synthétiques de Kotlin ne sont pas magiques et fonctionnent de manière très simple. Lorsque vous y accédez btn_K, cela demande getView().findViewById(R.id.btn_K).

Le problème est que vous y accédez trop tôt. getView()retours nulldans onCreateView. Essayez de le faire dans la onViewCreatedméthode:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}
Egor Neliuba
la source
2
Ça a marché!! Merci. Juste un bref avertissement pour référence future. J'ai eu une autre exception, et j'ai creusé un peu plus et il se trouve que l'exception de référence nulle provenait d'un rappel asynchrone du thread d'interface utilisateur où il tenterait d'accéder à la propriété synthétique, mais elle était déjà nulle à l'époque. Assurez-vous d'utiliser l' opérateur Safe Call (?.) Ou un autre opérateur de sécurité nulle. Cela aiderait également à conserver une référence de classe de la vue et à ne pas s'appuyer sur des propriétés synthétiques en dehors deonViewCreated()
solidak
2
Une question cependant - il génère un code différent pour l'activité et le fragment? Si nous utilisons une autre structure qui ne contient pas getView()ou qu'elle ne peut pas invoquer findViewById(), existe-t-il un moyen de contourner cela? Par exemple, lui apprendre quelle fonction retournera ma mise en page?
milosmns
7
Vous pouvez également y accéder comme rootView.btn_Ksi vous aviez une vue (et pas seulement par fragments, cela peut être fait partout)
Adib Faramarzi
Ça marche ! Cependant, cela devrait être plus souligné dans la documentation de Kotlin. Je n'ai pas remarqué cette méthode jusqu'à ce poste .. Merci quand même!
sokarcreative
2
Je l'ai toujours utilisé dans onViewCreated, mais toujours sur certains appareils (j'ai reçu le rapport de Crashlytics), il a obtenu l'exception "ne doit pas être nulle". La vue est là. Je gonfle la mise en page correcte, cela fonctionne sur mon appareil. C'est juste étrange de ne pas fonctionner sur un appareil aléatoire.
Arie Agung
9

Vous appelez cela btn_Ktrop tôt car à ce moment-là, il renvoie une valeur nulle et vous donne une exception de pointeur nul.

Vous pouvez utiliser ces vues par ce plugin synthétique dans la onActivityCreated()méthode qui est appelée juste après le onCreateView()cycle de vie du fragment.

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}
Luthra kashish
la source
Je veux juste souligner que pour certaines raisons, cette réponse a fonctionné pour moi alors que la réponse acceptée n'a pas fonctionné. Mes vues sont nulles onViewCreatedmais définies dans onActivityCreated. Je ne sais pas pourquoi cependant.
NathanL
6

Propriétés synthétiques générées par Kotlin Android Extensions plug - in a besoin d' un viewpour Fragment/Activityêtre mis en avant la main.

Dans votre cas, pour Fragment, vous devez utiliser view.btn_KdansonViewCreated

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 devez 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 savedInstanceStateparamètre doit être nullable Bundle?, et vérifiez également Importation des propriétés synthétiques

Il est pratique d'importer toutes les propriétés du widget pour une mise en page spécifique en une seule fois:

import kotlinx.android.synthetic.main.<layout>.*

Ainsi, si le nom du fichier de mise en page est activity_main.xml, nous importons kotlinx.android.synthetic.main.activity_main.*.

Si nous voulons appeler les propriétés synthétiques sur View, nous devons également importer kotlinx.android.synthetic.main.activity_main.view.*.

onmyway133
la source
3

la seule chose que vous devez faire est:

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
}
Rhusfer
la source
1
Oui, utilisez simplement val view = inflater.inflate() view.button.text = "caption".
CoolMind
C'est la meilleure réponse - la solution la plus soignée à coup sûr!
CacheMeOutside le
@CacheMeOutside non, car il s'agit toujours de code passe-partout rootView.subView.doSomething. Il est préférable d'utiliser les vues à partir deonViewCreated
user924
3

pas besoin de définir un objet compagnon, il suffit d'appeler chaque id 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
}
Rahul ShaRma
la source
1

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()

  }
abhilasha Yadav
la source
1
Pourquoi? Où avez-vous cette connaissance? Pourquoi pas à la onViewCreatedplace?
kuzdu
0

Dans mon cas, rien n'a fonctionné jusqu'à ce que je suive les conseils d' Otziii dans les commentaires. Nettoyez, reconstruisez (aucun redémarrage nécessaire), réexécutez l'application. Je n'avais pas non plus besoin d'y aller onActivityCreatedet j'ai juste onCreateViewfait l'affaire.

Une fois, j'ai également fait l'erreur de gonfler la mauvaise disposition, n'obtenant donc pas les contrôles attendus.

ZzZombo
la source
je l'ai vu se produire onActivityCreatedaussi
Jemshit Iskenderov
0

En l'ajoutant à la réponse de @Egor Neliuba, Oui chaque fois que vous appelez une vue sans référence, kotlinex recherche une rootView, et puisque vous êtes à l'intérieur d'un fragment et que le fragment n'a pas de getView()méthode. Par conséquent, cela pourrait jeterNullPointerException

Il y a deux façons de surmonter cela,

  • Soit vous remplacez 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.

Aziz
la source
-2
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)

    rootView?.findViewById<TextView>(R.id.mTextView)?.setOnClickListener{
        Log.d(TAG, "onViewCreated(): hello world");
    }
    //btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
    return rootView
}

}

** Ici, vous utilisez btn_K.setOnClickListener avant de trouver -Vous devez trouver l'élément XML sous votre code java / kotlin en utilisant findViewById puis et seulement vous pouvez effectuer une opération sur cette vue ou cet élément.

-Alors c'est pour cela que vous avez obtenu une exception de pointeur nul

**

Amit Walke
la source