NullPointerException al intentar acceder a vistas en un fragmento de Kotlin


240

¿Cómo usar Kotlin Android Extensions con Fragments? Si los uso adentro onCreateView(), obtengo esta NullPointerExceptionexcepción:

Causado por: java.lang.NullPointerException: intento de invocar el método virtual 'android.view.View android.view.View.findViewById (int)' en una referencia de objeto nulo

Aquí está el código de fragmento:

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

44
Si desea hacerlo en onCreateView, btn_K también será una propiedad en rootView. Podrías hacerrootView.btn_K.setOnClickListener
Makotosan

Gracias @Makotosan tu respuesta funcionó para mí.
Mahdi Bagjani

Limpiar, reconstruir y reiniciar Android Studio funcionó para mí
Otziii

@Otziii Este hilo se escribió por primera vez en 2015. La primera respuesta tiene 259 votos y fue aceptada. No creo que sea necesario agregar más respuestas.
solidak

2
@Solidak Recientemente tuve este problema, probé todas las respuestas y lo único que lo hizo funcionar fue lo que ahora comenté. Tenía una respuesta en este hilo, pero se votó negativamente, así que lo cambié a un comentario. Parece que las personas todavía tienen este problema, y ​​nadie mencionó limpiar y reiniciar.
Otziii

Respuestas:


443

Las propiedades sintéticas de Kotlin no son mágicas y funcionan de una manera muy simple. Cuando accedes btn_K, pide getView().findViewById(R.id.btn_K).

El problema es que está accediendo a él demasiado pronto. getView()vuelve nulla entrar onCreateView. Intenta hacerlo en el onViewCreatedmétodo:

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

2
¡¡Funcionó!! Gracias. Solo un aviso rápido para futuras referencias. Tuve otra excepción, profundicé un poco más y resultó que la excepción de referencia nula provenía de una devolución de llamada asíncrona al hilo de la interfaz de usuario donde intentaría acceder a la propiedad sintética, pero ya era nula en ese momento. Asegúrese de utilizar el operador Safe Call (?.) O algún otro operador de seguridad nulo. También ayudaría a mantener una referencia de clase de la vista y no confiar en propiedades sintéticas fuera deonViewCreated()
solidak

2
Sin embargo, una pregunta: ¿genera un código diferente para Actividad y Fragmento? Si usamos otra estructura que no contiene getView()o no puede invocar findViewById(), ¿hay alguna forma de evitarla? Por ejemplo, ¿enseñarle qué función devolverá mi diseño?
milosmns

77
También puede acceder a ella como rootView.btn_Ksi tuviera una vista (y no solo en fragmentos, esto se puede hacer en todas partes)
Adib Faramarzi

Funciona ! Sin embargo, debería estar más subrayado de la documentación de Kotlin. No noté este método hasta esta publicación. ¡Gracias de todos modos!
sokarcreative

2
Siempre lo usé en onViewCreated, pero aún en algún dispositivo (recibí el informe de Crashlytics) obtuvo la excepción "no debe ser nulo". La vista está ahí. Inflo el diseño correcto, funciona en mi dispositivo. Es extraño que no funcione en un dispositivo aleatorio.
Arie Agung

9

Está llamando a esto btn_Kdemasiado pronto ya que en ese momento devuelve un valor nulo y le está dando una excepción de puntero nulo.

Puede usar estas vistas mediante este complemento sintético en un onActivityCreated()método que se llama justo después onCreateView()del ciclo de vida de Fragment.

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

Solo quiero señalar que, por algunas razones, esta respuesta funcionó para mí, mientras que la respuesta aceptada no. Mis puntos de vista son nulos onViewCreatedpero luego definidos onActivityCreated. Sin embargo, no sé por qué.
NathanL

6

Propiedades sintéticos generados por Kotlin Extensiones Android plug-in necesita una viewpara Fragment/Activityser fijado de antemano.

En su caso, para Fragment, debe usar view.btn_KenonViewCreated

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 mejor, solo debe acceder a las propiedades sintéticas en 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`
}

Tenga en cuenta que el savedInstanceStateparámetro debe ser anulable Bundle?, y también marque Importar propiedades sintéticas

Es conveniente importar todas las propiedades del widget para un diseño específico de una sola vez:

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

Por lo tanto, si el nombre del archivo de diseño es activity_main.xml, importaríamos kotlinx.android.synthetic.main.activity_main.*.

Si queremos llamar a las propiedades sintéticas en View, también debemos importar kotlinx.android.synthetic.main.activity_main.view.*.


3

lo único que debes hacer es:

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í, solo úsalo val view = inflater.inflate() view.button.text = "caption".
CoolMind

Esta es la mejor respuesta: ¡la solución más segura!
CacheMeOutside

@CacheMeOutside no, porque todavía es código repetitivo rootView.subView.doSomething. Es mejor usar vistas a partir deonViewCreated
user924

3

no es necesario definir un objeto complementario, solo llame a cada id por una vista como

 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

En Fragments, escriba su código en 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
¿Por qué? ¿Dónde tienes este conocimiento? ¿Por qué no en su onViewCreatedlugar?
kuzdu

0

En mi caso, nada funcionó hasta que seguí el consejo de Otziii en los comentarios. Limpie, reconstruya (no es necesario reiniciar), vuelva a ejecutar la aplicación. Tampoco necesitaba ir onActivityCreatedy solo onCreateViewhice el truco.

Una vez también cometí el error de inflar el diseño incorrecto, por lo que obviamente no obtuve los controles esperados.


he visto que esto ocurra en onActivityCreateddemasiado
Jemshit Iskenderov

0

Agregándolo a la respuesta de @Egor Neliuba, Sí, cada vez que llama a una vista sin referencia, kotlinex busca un rootView, y dado que está dentro de un fragmento y el fragmento no tiene getView() método. Por lo tanto, podría arrojarNullPointerException

Hay dos formas de superar esto,

  • O anula onViewCreated()como se mencionó
  • O bien, si desea vincular vistas en otra clase (por ejemplo, anónimo), simplemente puede crear una función de extensión como esta,

    fun View.bindViews(){...}

El segundo enfoque es útil cuando tiene un solo fragmento con múltiples comportamientos.


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

}

** Aquí está utilizando btn_K.setOnClickListener antes de buscar. Debe encontrar el elemento xml en su código java / kotlin utilizando findViewById y luego solo puede realizar la operación en esa vista o elemento.

-Así que es por eso que tienes puntero nulo

** **

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.