NullPointerException при спробі доступу до представлень у фрагменті Котліна


240

Як використовувати розширення Android від Kotlin з 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
@Solidak У мене була ця проблема нещодавно, я спробував усі відповіді, і єдине, що змусило її працювати, це те, що я зараз прокоментував. У мене була відповідь на цю тему, але вона просто зникла, тому я змінив її на коментар. Схоже, у людей все ще виникає ця проблема, і ніхто не згадав про очищення та перезапуск.
Otziii

Відповіді:


443

Синтетичні властивості Котліна не є магічними і працюють дуже просто. Коли ви отримуєте доступ 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
Хоча одне питання - він генерує інший код для діяльності та фрагмента? Якщо ми використовуємо іншу структуру, яка не містить getView()або вона не може викликати findViewById(), чи є спосіб її обійти? Наприклад, навчіть її, яка функція поверне мій макет?
milosmns

7
Ви також можете отримати доступ до нього, як rootView.btn_Kякщо у вас є вид (і не лише фрагменти, це можна зробити скрізь)
Адіб Фарамарці

Це працює ! Однак це повинно бути більш підкреслено в документації Котліна. Я не помітив цього методу до цієї публікації .. Все одно дякую!
sokarcreative

2
Я завжди використовував його в onViewCreate, але все ж на деяких пристроях (я отримав звіт від Crashlytics) він отримав "не повинно бути нульовим" винятком. Вид є. Я надуваю правильний макет, він працює на моєму пристрої. Це просто дивно не працює на випадковому пристрої.
Arie Agung

9

Ви зателефонуєте цьому btn_Kзанадто рано, оскільки в цей час він повертає нуль і надає вам Null Pointer Exception.

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

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

Я просто хочу зазначити, що з якихось причин ця відповідь спрацювала для мене, поки прийнята відповідь не зробила. Мої погляди недійсні, onViewCreatedале потім визначені в onActivityCreated. Не знаю, чому все-таки.
NathanL

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параметр повинен бути зведений на нуль 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

У "Фрагменти" будь ласка, напишіть свій код у onActivityCreate: -

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

У моєму випадку нічого не вийшло, поки я не дотримувався порад від Отзії в коментарях. Очистіть, відновіть (не потрібно перезавантажувати), перезапустіть додаток. Мені теж не потрібно було йти 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.