NullPointerException beim Versuch, auf Ansichten in einem Kotlin-Fragment zuzugreifen


238

Wie verwende ich Kotlin Android Extensions mit Fragments? Wenn ich sie im Inneren verwende onCreateView(), erhalte ich diese NullPointerExceptionAusnahme:

Auslöser: java.lang.NullPointerException: Versuch, die virtuelle Methode 'android.view.View android.view.View.findViewById (int)' für eine Nullobjektreferenz aufzurufen

Hier ist der Fragmentcode:

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

4
Wenn Sie dies in onCreateView tun möchten, ist btn_K auch eine Eigenschaft in rootView. Sie könnten tunrootView.btn_K.setOnClickListener
Makotosan

Danke @Makotosan, deine Antwort hat bei mir funktioniert.
Mahdi Bagjani

Bereinigen, neu
erstellen

@Otziii Dieser Thread wurde erstmals im Jahr 2015 geschrieben. Die erste Antwort hat 259 Stimmen und wurde angenommen. Ich denke nicht, dass es notwendig ist, weitere Antworten hinzuzufügen.
Solidak

2
@ Solidak Ich hatte dieses Problem vor kurzem, versuchte alle Antworten und das einzige, was es funktionierte, war das, was ich jetzt kommentierte. Ich hatte eine Antwort auf diesen Thread, aber er wurde gerade herabgestimmt, also habe ich ihn in einen Kommentar geändert. Es scheint, als hätten die Leute immer noch dieses Problem, und niemand hat erwähnt, dass es bereinigt und neu gestartet werden soll.
Otziii

Antworten:


441

Die synthetischen Eigenschaften von Kotlin sind keine Zauberei und funktionieren auf sehr einfache Weise. Wenn Sie zugreifen btn_K, fordert es getView().findViewById(R.id.btn_K).

Das Problem ist, dass Sie zu früh darauf zugreifen. getView()kehrt nullin zurück onCreateView. Versuchen Sie es in der onViewCreatedMethode:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}

2
Es funktionierte!! Vielen Dank. Nur ein kurzes Heads-up zum späteren Nachschlagen. Ich hatte eine andere Ausnahme, und ich habe etwas tiefer gegraben, und es stellte sich heraus, dass die Nullreferenzausnahme von einem asynchronen Rückruf an den UI-Thread stammte, in dem versucht wurde, auf die synthetische Eigenschaft zuzugreifen, aber zu diesem Zeitpunkt war sie bereits null. Stellen Sie sicher, dass Sie den Safe Call- Operator (?.) Oder einen anderen Null-Sicherheitsoperator verwenden. Es würde auch helfen, eine Klassenreferenz der Ansicht onViewCreated()
beizubehalten

2
Eine Frage: Es wird ein anderer Code für Aktivität und Fragment generiert. Wenn wir eine andere Struktur verwenden, die keine enthält getView()oder nicht aufrufen kann findViewById(), gibt es eine Möglichkeit, diese zu umgehen? Lehren Sie zum Beispiel, welche Funktion mein Layout zurückgibt.
Milosmns

7
Sie können auch darauf zugreifen, rootView.btn_Kwenn Sie eine Ansicht haben (und nicht nur in Fragmenten, dies kann überall erfolgen)
Adib Faramarzi

Es klappt ! Es sollte jedoch in der Kotlin-Dokumentation stärker unterstrichen werden. Ich habe diese Methode erst in diesem Beitrag bemerkt. Trotzdem danke!
Sokarcreative

2
Ich habe es immer in onViewCreated verwendet, aber auf einem Gerät (ich habe den Bericht von Crashlytics erhalten) wurde die Ausnahme "darf nicht null sein" angezeigt. Die Aussicht ist da. Ich blase das richtige Layout auf, es funktioniert auf meinem Gerät. Es ist nur seltsam, nicht auf einem zufälligen Gerät zu arbeiten.
Arie Agung

9

Sie rufen dies btn_Kzu früh auf, da zu diesem Zeitpunkt eine Null zurückgegeben wird und Sie eine Null-Zeiger-Ausnahme erhalten.

Sie können diese Ansichten mit dieser synthetischen Plugin-In- onActivityCreated()Methode verwenden, die unmittelbar nach dem onCreateView()Fragment-Lebenszyklus aufgerufen wird .

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}

Ich möchte nur darauf hinweisen, dass diese Antwort aus bestimmten Gründen für mich funktioniert hat, während die akzeptierte Antwort dies nicht tat. Meine Ansichten sind null in, onViewCreatedaber dann definiert in onActivityCreated. Ich weiß aber nicht warum.
NathanL

6

Synthetische Eigenschaften erzeugt durch Kotlin Android Extensions Plugin muss eine viewfür Fragment/Activityvorher festgelegt werden.

In Ihrem Fall für Fragment, müssen Sie verwenden view.btn_KinonViewCreated

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
}

Oder besser, Sie sollten nur auf synthetische Eigenschaften in zugreifen 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`
}

Beachten Sie, dass der savedInstanceStateParameter nullwertfähig sein sollte Bundle?, und aktivieren Sie das Kontrollkästchen Synthetische Eigenschaften importieren

Es ist praktisch, alle Widget-Eigenschaften für ein bestimmtes Layout auf einmal zu importieren:

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

Wenn der Layout-Dateiname activity_main.xml lautet, werden wir importieren kotlinx.android.synthetic.main.activity_main.*.

Wenn wir die synthetischen Eigenschaften in View aufrufen möchten, sollten wir auch importieren kotlinx.android.synthetic.main.activity_main.view.*.


3

Das einzige, was Sie tun müssen, ist:

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
}

1
Ja, einfach benutzen val view = inflater.inflate() view.button.text = "caption".
CoolMind

Dies ist die beste Antwort - mit Sicherheit die beste Lösung!
CacheMeOutside

@CacheMeOutside nein, weil es immer noch Boilerplate-Code ist rootView.subView.doSomething. Es ist besser, Ansichten abonViewCreated
user924

3

Es ist nicht erforderlich, ein Begleitobjekt zu definieren. Rufen Sie einfach jede ID über eine Ansicht wie auf

 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
}

1

In Fragmente schreiben Sie bitte Ihren Code in 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()

  }

1
Warum? Wo haben Sie dieses Wissen? Warum nicht onViewCreatedstattdessen?
Kuzdu

0

In meinem Fall hat nichts funktioniert, bis ich den Ratschlägen von Otziii in den Kommentaren gefolgt bin . Bereinigen, neu erstellen (kein Neustart erforderlich), App erneut ausführen. Ich musste auch nicht mitgehen onActivityCreatedund habe einfach onCreateViewden Trick gemacht.

Einmal habe ich auch den Fehler gemacht, ein falsches Layout aufzublasen, wodurch ich offensichtlich nicht die erwarteten Steuerelemente bekam.


Ich habe es geschieht in gesehen onActivityCreatedzu
Jemshit Iskenderov

0

Wenn Sie es zu @Egor Neliubas Antwort hinzufügen: Ja, wenn Sie eine Ansicht ohne Referenz aufrufen, sucht kotlinex nach einer rootView, und da Sie sich in einem Fragment befinden und das Fragment keine getView()Methode hat. Deshalb könnte es werfenNullPointerException

Es gibt zwei Möglichkeiten, dies zu überwinden:

  • Entweder überschreiben Sie onViewCreated()wie erwähnt
  • Oder Wenn Sie Ansichten in einer anderen Klasse (z. B. anonym) binden möchten, können Sie einfach eine Erweiterungsfunktion wie diese erstellen:

    fun View.bindViews(){...}

Der zweite Ansatz ist hilfreich, wenn Sie ein einzelnes Fragment mit mehreren Verhaltensweisen haben.


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

}}

** Hier verwenden Sie btn_K.

- Deshalb haben Sie die Ausführung eines Nullzeigers erhalten

** **.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.