NullPointerException ao tentar acessar visualizações em um fragmento Kotlin

240

Como usar as extensões Kotlin Android com Fragments? Se eu usá-los dentro onCreateView(), recebo esta NullPointerExceptionexceção:

Causado por: java.lang.NullPointerException: tentativa de chamar o método virtual 'android.view.View android.view.View.ind.findViewById (int)' em uma referência de objeto nulo

Aqui está o código do 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
    }
}
`
solidak
fonte
4
Se você quiser fazer isso no onCreateView, btn_K também será uma propriedade no rootView. Você poderia fazerrootView.btn_K.setOnClickListener
Makotosan
Obrigado @Makotosan, sua resposta funcionou para mim.
Mahdi Bagjani
Limpo, reconstruir e reiniciar estúdio Android funcionou para mim
Otziii
@Otziii Este tópico foi escrito pela primeira vez em 2015. A primeira resposta tem 259 votos e foi aceita. Não acho que seja necessário adicionar mais respostas.
Solidak 26/10/19
2
@ Solidak Eu tive esse problema recentemente, tentei todas as respostas e a única coisa que fez funcionar foi o que eu comentei agora. Eu tinha uma resposta sobre este tópico, mas ele acabou com o voto negativo, então mudei para um comentário. Parece que as pessoas ainda estão tendo esse problema e ninguém foi mencionado para limpar e reiniciar.
Otziii 26/10/19

Respostas:

443

As propriedades sintéticas de Kotlin não são mágicas e funcionam de uma maneira muito simples. Quando você acessa btn_K, ele pede getView().findViewById(R.id.btn_K).

O problema é que você está acessando muito cedo. getView()retorna nullem onCreateView. Tente fazer isso no onViewCreatedmétodo:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}
Egor Neliuba
fonte
2
Funcionou!! Obrigado. Apenas um alerta rápido para referência futura. Eu tive outra exceção e me aprofundou um pouco mais, e a exceção nula de referência vinha de um retorno de chamada assíncrona para o thread da interface do usuário em que tentava acessar a propriedade sintética, mas já era nulo na época. Certifique-se de usar o operador Chamada segura (?.) Ou algum outro operador de segurança nula. Ele também ajuda a manter uma referência de classe da visão e não depender de propriedades sintéticas foraonViewCreated()
solidak
2
Porém, uma pergunta - gera código diferente para Atividade e Fragmento? Se usarmos outra estrutura que não contém getView()ou não pode ser chamada findViewById(), existe uma maneira de contornar isso? Por exemplo, ensine qual função retornará meu layout?
milosmns
7
Você também pode acessá-lo como rootView.btn_Kse você tem uma vista (e não apenas em fragmentos, isso pode ser feito em todos os lugares)
Adib Faramarzi
Funciona ! No entanto, deve ser mais sublinhado na documentação do Kotlin. Eu não notei esse método até este post .. Obrigado mesmo assim!
sokarcreative
2
Eu sempre o usei no onViewCreated, mas ainda em algum dispositivo (recebi o relatório da Crashlytics), ele recebeu a exceção "não deve ser nula". A vista está lá. Infle o layout correto, ele funciona no meu dispositivo. É estranho não funcionar em dispositivo aleatório.
Arie Agung
9

Você está chamando isso btn_Kmuito cedo, pois nesse momento ele retorna um nulo e está fornecendo uma exceção de ponteiro nulo.

Você pode usar essas visualizações por este plug-in sintético no onActivityCreated()método chamado logo após o onCreateView()ciclo de vida do Fragment.

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}
Kuthish luthra
fonte
Eu só quero ressaltar que, por alguns motivos, essa resposta funcionou para mim, enquanto a resposta aceita não. Minhas visualizações são nulas, onViewCreatedmas definidas em onActivityCreated. Não sei porque.
NathanL
6

Propriedades sintéticos gerados por Kotlin extensões Android plug-in precisa de um viewpara Fragment/Activityser definido antes da mão.

No seu caso, para Fragment, você precisa usar view.btn_KnoonViewCreated

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 melhor, você deve acessar apenas propriedades sintéticas em 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`
}

Observe que o savedInstanceStateparâmetro deve ser anulável Bundle?e marque Importando propriedades sintéticas

É conveniente importar todas as propriedades do widget para um layout específico de uma só vez:

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

Portanto, se o nome do arquivo do layout for activity_main.xml, importaremos kotlinx.android.synthetic.main.activity_main.*.

Se quisermos chamar as propriedades sintéticas no View, também devemos importar kotlinx.android.synthetic.main.activity_main.view.*.

onmyway133
fonte
3

a única coisa que você precisa fazer é:

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
fonte
1
Sim, apenas use val view = inflater.inflate() view.button.text = "caption".
CoolMind 17/07/2018
Esta é a melhor resposta - a melhor solução, com certeza!
CacheMeOutside
@CacheMeOutside não, porque ainda é o código padrão rootView.subView.doSomething. É melhor usar visualizações a partir deonViewCreated
user924 13/02/19
3

não é necessário definir o objeto complementar, basta chamar todos os IDs por uma visualização 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
}
Rahul ShaRma
fonte
1

Em Fragments, escreva seu código em 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
fonte
1
Por quê? Onde tem esse conhecimento? Por que não onViewCreated?
kuzdu
0

No meu caso, nada funcionou até eu seguir o conselho de Otziii nos comentários. Limpe, reconstrua (não é necessário reiniciar), execute novamente o aplicativo. Eu também não precisava ir onActivityCreatede apenas onCreateViewfiz o truque.

Uma vez, também cometi o erro de aumentar o layout errado, não obtendo obviamente os controles esperados.

ZzZombo
fonte
eu tenho visto isso acontecer em onActivityCreateddemasiado
Jemshit Iskenderov
0

Adicionando-o à resposta de @Egor Neliuba, Sim, sempre que você chama uma visualização sem referência, o kotlinex procura um rootView e, como você está dentro de um fragmento, o fragmento não possui getView()método. Portanto, pode jogarNullPointerException

Existem duas maneiras de superar isso,

  • Você substitui onViewCreated()como mencionado
  • Ou, se quiser vincular visualizações em outra classe (por exemplo, anônima), você pode simplesmente criar uma função de extensão como esta,

    fun View.bindViews(){...}

A segunda abordagem é útil quando você possui um único fragmento com comportamento múltiplo.

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

}

** Aqui você está usando btn_K.setOnClickListener antes de encontrar -Você precisa encontrar o elemento xml no seu código java / kotlin usando findViewById e somente então você pode executar a operação nessa exibição ou elemento.

-Então é por isso que a execução de ponteiro nulo você tem

**

Amit Walke
fonte