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

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

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

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

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

Ответ 1

Синтетические свойства Kotlin не волшебны и работают очень просто. Когда вы btn_K доступ к 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

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

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

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

Ответ 3

Синтетические свойства, сгенерированные плагином Kotlin 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.*.

Ответ 4

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

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
}

Ответ 5

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

 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
}

Ответ 6

Во фрагментах, пожалуйста, напишите свой код в 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()

  }

Ответ 7

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

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

Ответ 8

Синтетические свойства Kotlin не волшебны и работают очень просто. Когда вы получаете доступ к btn_K, он вызывает 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
}

soo, если вы используете обратные вызовы интерфейсов, вы получите NPE, потому что представление не будет доступно в это время → вы должны переопределить метод getView()

override fun getView(): View? {
    return mRootView
}

и всегда используйте свои взгляды в onViewCreated()

if (mCreatedView == null) {
   mCreatedView = view
   btn_K.setOnClickListener{
        //------------do something
   }
 }

Ответ 9

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

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

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

    fun View.bindViews(){...}

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

Ответ 10

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, прежде чем обнаружите, что -You должен найти элемент формы xml в вашем коде java/kotlin, используя затем findViewById, и только тогда вы можете выполнять операции с этим представлением или элементом.

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

**