móviles - manual de programacion android pdf
NullPointerException al intentar acceder a vistas en un fragmento de Kotlin (10)
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 el método
getView()
.
Por lo tanto, podría arrojar
NullPointerException
Hay dos formas de superar esto,
-
O anula
onViewCreated()
como se mencionó -
O bien, si desea vincular vistas en otra clase (por ejemplo, anónima), 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.
¿Cómo usar Kotlin Android Extensions con
Fragment
s?
Si los uso dentro de
onCreateView()
, obtengo esta excepción
NullPointerException
:
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
}
}
`
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()
}
En mi caso, nada funcionó hasta que seguí el consejo de
en los comentarios.
Limpiar, reconstruir (no es necesario reiniciar), vuelva a ejecutar la aplicación.
Tampoco necesitaba
onActivityCreated
y solo
onCreateView
hizo el truco.
Una vez también cometí el error de inflar el diseño incorrecto, por lo que obviamente no obtuve los controles esperados.
Está llamando a este
btn_K
demasiado 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 el método
onActivityCreated()
que se llama justo después de
onCreateView()
del ciclo de vida de Fragment.
onActivityCreated()
{
super.onActivityCreated(savedInstanceState)
btn_K.setOnClickListener{}
}
Las propiedades sintéticas de Kotlin no son mágicas y funcionan de una manera muy simple.
Cuando accede a
btn_K
, llama a
getView().findViewById(R.id.btn_K)
.
El problema es que está accediendo demasiado pronto.
getView()
devuelve
null
en
onCreateView
.
Intenta hacerlo en el método
onViewCreated
:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}
Las propiedades sintéticas de Kotlin no son mágicas y funcionan de una manera muy simple.
Cuando accede a btn_K, llama a
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
}
Entonces, si está utilizando devoluciones de llamada de interfaces, obtendrá NPE porque la vista no estará disponible en este momento -> debe anular el método
getView()
override fun getView(): View? {
return mRootView
}
y siempre use sus vistas en
onViewCreated()
if (mCreatedView == null) {
mCreatedView = view
btn_K.setOnClickListener{
//------------do something
}
}
Las propiedades sintéticas generadas por el
complemento Kotlin Android Extensions
necesitan una
view
para que
Fragment/Activity
se configure de antemano.
En su caso, para
Fragment
, debe usar
view.btn_K
en
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
}
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 parámetro
savedInstanceState
debe ser nulo
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 deberíamos importar
kotlinx.android.synthetic.main.activity_main.view.*.
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
}
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
}
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 encontrar -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
** **