NullPointerException durante il tentativo di accedere alle viste in un frammento di Kotlin


240

Come usare le estensioni Android di Kotlin con Fragments? Se li uso all'interno onCreateView(), ottengo questa NullPointerExceptioneccezione:

Causato da: java.lang.NullPointerException: tentativo di invocare il metodo virtuale "android.view.View android.view.View.findViewById (int)" su un riferimento a oggetto null

Ecco il codice del frammento:

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
Se vuoi farlo in onCreateView, btn_K sarà anche una proprietà su rootView. Si potrebbe farerootView.btn_K.setOnClickListener
Makotosan il

Grazie @Makotosan la tua risposta ha funzionato per me.
Mahdi Bagjani,

Pulire, ricostruire e riavviare Android Studio ha funzionato per me
Otziii,

@Otziii Questa discussione è stata scritta per la prima volta nel 2015. La prima risposta ha 259 voti ed è stata accettata. Non credo sia necessario aggiungere altre risposte.
solidak,

2
@Solidak Ho avuto questo problema di recente, ho provato tutte le risposte e l'unica cosa che l'ha fatto funzionare è stato quello che ho commentato ora. Ho avuto una risposta su questo thread, ma è stato appena votato, quindi l'ho cambiato in un commento. Sembra che le persone stiano ancora riscontrando questo problema e nessuno ha menzionato di pulire e riavviare.
Otziii,

Risposte:


443

Le proprietà sintetiche di Kotlin non sono magiche e funzionano in modo molto semplice. Quando accedi btn_K, richiedegetView().findViewById(R.id.btn_K) .

Il problema è che stai accedendo troppo presto. getView()ritorna nullin onCreateView. Prova a farlo nel onViewCreatedmetodo:

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

2
Ha funzionato!! Grazie. Solo un rapido avvertimento per riferimento futuro. Ho avuto un'altra eccezione, ho scavato un po 'più a fondo e si scopre che l'eccezione di riferimento Null proveniva da un callback asincrono al thread dell'interfaccia utente dove avrebbe tentato di accedere alla proprietà sintetica, ma era già nulla al momento. Assicurarsi di utilizzare l' operatore Chiamata sicura (?.) O qualche altro operatore di sicurezza null. onViewCreated()
Aiuterebbe

2
Una domanda però: genera codice diverso per Activity e Fragment? Se utilizziamo un'altra struttura che non contiene getView()o che non può invocare findViewById(), c'è un modo per aggirarla? Ad esempio, insegnargli quale funzione restituirà il mio layout?
milosmns,

7
Puoi anche accedervi come rootView.btn_Kse avessi una vista (e non solo in frammenti, questo può essere fatto ovunque)
Adib Faramarzi,

Funziona ! Tuttavia, dovrebbe essere più sottolineato dalla documentazione di Kotlin. Non ho notato questo metodo fino a questo post .. Grazie comunque!
sokarcreative,

2
L'ho sempre usato in onViewCreated, ma ancora su alcuni dispositivi (ho ricevuto il rapporto da Crashlytics) ha ottenuto un'eccezione "non deve essere nulla". La vista è lì. Gonfio il layout corretto, funziona sul mio dispositivo. È strano non funzionare su un dispositivo casuale.
Arie Agung,

9

Lo stai chiamando btn_K troppo presto poiché in quel momento restituisce un valore nullo e ti dà un'eccezione del puntatore null.

Puoi utilizzare queste visualizzazioni con questo plugin sintetico nel onActivityCreated()metodo che viene chiamato subito dopo il onCreateView()ciclo di vita di Fragment.

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

Voglio solo sottolineare che per alcuni motivi, questa risposta ha funzionato per me, mentre la risposta accettata no. Le mie opinioni sono nulle onViewCreatedma poi definite in onActivityCreated. Non so perché però.
NathanL

6

Proprietà sintetici generati da Kotlin estensioni Android plug-in ha bisogno di una viewperFragment/Activity essere impostata prima della mano.

Nel tuo caso, per Fragment, è necessario utilizzare 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
}

O meglio, dovresti solo accedere alle proprietà sintetiche in 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`
}

Si prega di notare che savedInstanceState parametro deve essere nullable Bundle?e selezionare anche Importazione di proprietà sintetiche

È conveniente importare tutte le proprietà del widget per un layout specifico in una volta sola:

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

Pertanto, se il nome file del layout è activity_main.xml, verremmo importati kotlinx.android.synthetic.main.activity_main.*.

Se vogliamo chiamare le proprietà sintetiche su View, dovremmo anche importare kotlinx.android.synthetic.main.activity_main.view.*.


3

l'unica cosa che devi fare è:

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
Sì, basta usare val view = inflater.inflate() view.button.text = "caption".
CoolMind,

Questa è la risposta migliore, sicuramente la soluzione più accurata!
CacheMeOutside

@CacheMeOutside no, perché è ancora codice boilerplate rootView.subView.doSomething. È meglio utilizzare le viste a partire daonViewCreated
user924,

3

non c'è bisogno di definire un oggetto associato, basta chiamare ogni id con una vista simile

 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 Fragments scrivi il tuo codice 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
Perché? Dove hanno questa conoscenza? Perché non onViewCreatedinvece?
Kuzdu,

0

Nel mio caso nulla ha funzionato fino a quando non ho seguito i consigli di Otziii nei commenti. Pulisci, ricostruisci (non è necessario riavviare), riesegui l'app. Inoltre non ho avuto bisogno di andare con onActivityCreatede soloonCreateView ho fatto il trucco.

Una volta ho anche fatto l'errore di gonfiare il layout sbagliato, senza ovviamente ottenere i controlli previsti.


l'ho visto accadere onActivityCreatedanche in
Jemshit Iskenderov

0

Aggiungendolo alla risposta di @Egor Neliuba, Sì ogni volta che chiami una vista senza riferimento, kotlinex cerca un rootView e poiché sei all'interno di un frammento e il frammento non ha getView()metodo. Pertanto potrebbe lanciareNullPointerException

Ci sono due modi per superare questo,

  • O esegui onViewCreated()l' override come indicato
  • Oppure, se desideri associare visualizzazioni in un'altra classe (ad esempio anonimo), puoi semplicemente creare una funzione di estensione come questa,

    fun View.bindViews(){...}

Il secondo approccio è utile quando si dispone di un singolo frammento con comportamento multiplo.


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

}

** Qui stai usando btn_K.setOnClickListener prima di trovare -Devi trovare l'elemento forma xml nel tuo codice java / kotlin usando findViewById e quindi solo tu puoi eseguire operazioni su quella vista o elemento.

-E 'per questo che hai eseguito l'esecuzione del puntatore null

**

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.