NullPointerException при попытке доступа к представлениям во фрагменте Kotlin


240

Как использовать Kotlin Android Extensions с Fragments? Если я использую их внутри onCreateView(), я получаю это NullPointerExceptionисключение:

Вызывается: java.lang.NullPointerException: попытка вызвать виртуальный метод 'android.view.View android.view.View.findViewById (int)' для ссылки на пустой объект

Вот фрагмент кода:

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
Если вы хотите сделать это в onCreateView, btn_K будет также свойством rootView. Вы могли бы сделатьrootView.btn_K.setOnClickListener
Макотосан

Спасибо @Makotosan ваш ответ сработал для меня.
Махди Баджани

Очистить, восстановить и перезапустить Android-студия работала для меня
Otziii

@Otziii Эта тема была впервые написана в 2015 году. Первый ответ получил 259 голосов и был принят. Я не считаю необходимым добавлять больше ответов.
солидак

2
@ Солидак У меня недавно была эта проблема, перепробовал все ответы, и единственное, что заставило ее работать, это то, что я сейчас прокомментировал. У меня был ответ на эту ветку, но за него просто проголосовали, поэтому я изменил его на комментарий. Похоже, что люди все еще имеют эту проблему, и никто не упомянул, чтобы очистить и перезапустить.
Otziii

Ответы:


443

Синтетические свойства Kotlin не волшебны и работают очень просто. Когда вы получаете доступ btn_K, он требует getView().findViewById(R.id.btn_K).

Проблема в том, что вы получаете к нему доступ слишком рано. getView()возвращается nullв onCreateView. Попробуйте сделать это в onViewCreatedметоде:

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

2
Это сработало!! Спасибо. Просто быстрый хедз-ап для дальнейшего использования. У меня было еще одно исключение, и я выкопал немного глубже, и оказалось, что исключение Null Reference Exception передавалось из асинхронного обратного вызова в поток пользовательского интерфейса, где он пытался получить доступ к синтетическому свойству, но в то время оно уже было нулевым. Обязательно используйте оператора безопасного вызова (?) Или другого нулевого оператора безопасности. Это также помогло бы сохранить ссылку на класс представления и не полагаться на синтетические свойства внеonViewCreated()
solidak

2
Хотя один вопрос - он генерирует другой код для Activity и Fragment? Если мы используем другую структуру, которая не содержит getView()или не может вызвать findViewById(), есть ли способ обойти это? Например, научите его, какая функция вернет мой макет?
milosmns

7
Вы также можете получить к нему доступ, как rootView.btn_Kесли бы у вас было представление (и не только по фрагментам, это можно сделать везде)
Адиб

Оно работает ! Однако это должно быть более подчеркнуто из документации Kotlin. Я не заметил этот метод до этого поста .. В любом случае, спасибо!
sokarcreative

2
Я всегда использовал его в onViewCreated, но все же на каком-то устройстве (я получил отчет от Crashlytics) он получил исключение «не должно быть нулевым». Вид есть. Я раздуваю правильную раскладку, она работает на моем устройстве. Просто странно не работать на случайном устройстве.
Арье Агунг

9

Вы вызываете это btn_Kслишком рано, так как в это время он возвращает ноль и дает вам исключение нулевого указателя.

Вы можете использовать эти представления этим синтетическим плагином в onActivityCreated()методе, который вызывается сразу после onCreateView()жизненного цикла фрагмента.

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

Я просто хочу отметить, что по некоторым причинам этот ответ работал для меня, в то время как принятый ответ - нет. Мои взгляды нулевые, onViewCreatedно затем определены в onActivityCreated. Хотя не знаю почему.
НатанЛ

6

Синтетические свойства , порождаемые Котлин Android Extensions плагина нужно viewдля , Fragment/Activityчтобы установить , прежде чем руки.

В вашем случае, для Fragment, вам нужно использовать view.btn_Kв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
}

Или лучше, вы должны получить доступ только к синтетическим свойствам в 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`
}

Обратите внимание, что savedInstanceStateпараметр должен иметь значение NULL Bundle?, а также установите флажок Импорт синтетических свойств.

Удобно импортировать все свойства виджета для определенного макета за один раз:

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

Таким образом, если имя файла макета - activity_main.xml, мы импортируем kotlinx.android.synthetic.main.activity_main.*.

Если мы хотим вызвать синтетические свойства в View, мы также должны импортировать kotlinx.android.synthetic.main.activity_main.view.*.


3

единственное, что вам нужно сделать, это:

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
Да, просто используйте val view = inflater.inflate() view.button.text = "caption".
CoolMind

Это лучший ответ - самое лучшее решение!
CacheMeOutside

@CacheMeOutside нет, потому что это все еще стандартный код rootView.subView.doSomething. Лучше использовать представления, начиная сonViewCreated
user924

3

нет необходимости определять сопутствующий объект, просто вызовите каждый идентификатор с помощью вида

 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

Во фрагментах, пожалуйста, напишите свой код в 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
Зачем? Откуда эти знания? Почему бы не onViewCreatedвместо этого?
Кузду

0

В моем случае ничего не получалось, пока я не последовал совету Otziii в комментариях. Очистите, восстановите (перезагрузка не требуется), перезапустите приложение. Мне также не нужно было идти onActivityCreatedи просто onCreateViewсделал свое дело.

Однажды я также допустил ошибку, надувая неправильную компоновку, таким образом, очевидно, не получив ожидаемые элементы управления.


Я видел, как это происходит в onActivityCreatedтоже время
Джемшит Искендеров

0

Добавляя его к ответу @Egor Neliuba, да, всякий раз, когда вы вызываете представление без ссылки, kotlinex ищет rootView, и поскольку вы находитесь внутри фрагмента, а у фрагмента нет getView()метода. Поэтому это может броситьNullPointerException

Есть два способа преодолеть это,

  • Либо вы отменяете, onViewCreated()как упоминалось
  • Или, если вы хотите связать представления в каком-то другом классе (скажем, анонимном), вы можете просто создать функцию расширения, например:

    fun View.bindViews(){...}

Второй подход полезен, когда у вас есть один фрагмент с множественным поведением.


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

}

** Здесь вы используете btn_K.setOnClickListener перед поиском. Вы должны найти элемент xml в вашем коде java / kotlin, используя findViewById тогда и только тогда вы можете выполнять операции с этим представлением или элементом.

-Таким образом, вы получили исключение нулевого указателя

**

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.