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 вот почему вы получили исключение нулевого указателя
**