尝试访问Kotlin片段中的视图时发生NullPointerException


239

如何与一起使用Kotlin Android扩展Fragment?如果我在内部使用它们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 '16

谢谢@Makotosan,您的回答对我有用。
Mahdi Bagjani

清理,重建并重新启动Android Studio为我工作
Otziii,

@Otziii该主题最早于2015年编写。第一个答案有259票并被接受。我认为没有必要添加更多答案。
solidak

2
@Solidak我最近遇到了这个问题,尝试了所有答案,唯一起作用的就是我现在评论的内容。我对此线程有一个答案,但是它只是被否决了,所以我将其更改为评论。似乎人们仍然遇到此问题,没有人提到要清理并重新启动。
Otziii '18 -10-26

Answers:


442

Kotlin的合成特性不是神奇的,并且可以通过非常简单的方式起作用。当您访问时btn_K,它要求getView().findViewById(R.id.btn_K)

问题是您访问它太早了。getView()返回nullonCreateView。尝试通过以下onViewCreated方法进行操作:

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

2
有效!!谢谢。敬请注意,以备将来参考。我还有另一个异常,我进行了更深入的研究,结果发现Null Reference Exception来自异步回调到UI线程,在UI线程中它将尝试访问合成属性,但当时它已经为null。确保使用“ 安全呼叫”运算符(?。)或其他一些空的安全运算符。这也将有助于保留视图的类引用,而不依赖于onViewCreated()
solidak

2
但是有一个问题-它为Activity和Fragment生成了不同的代码?如果我们使用不包含getView()或无法调用的其他结构findViewById(),是否有解决方法?例如,教它哪个函数将返回我的布局?
milosmns 16-10-30

7
您也可以像rootView.btn_K有视图一样访问它(不仅仅是片段,还可以在任何地方完成)
Adib Faramarzi

有用 !但是,应该从Kotlin文档中更加强调。直到这篇文章,我才注意到这种方法。
sokarcreative

2
我一直在onViewCreated中使用它,但是仍然在某些设备上(我从Crashlytics获得了报告),它出现了“必须不为null”异常。视图在那里。我会增加正确的布局,它可以在我的设备上运行。奇怪的是不能在随机设备上工作。
阿里·阿贡

9

您调用它的btn_K时间太早,因为它返回一个null并给您Null Pointer Exception。

您可以通过此合成插件在Fragment生命周期onActivityCreated()之后调用的方法中使用这些视图onCreateView()

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

我只想指出,由于某些原因,这个答案对我有用,而被接受的答案却没有。我的视图在中为null,onViewCreated然后在中定义onActivityCreated。不知道为什么。
NathanL '18

6

Kotlin Android扩展插件生成的综合属性需要先设置viewfor Fragment/Activity

在你的情况下,Fragment你需要使用view.btn_KonViewCreated

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

无需定义伴侣对象,只需通过如下视图调用每个id

 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呢?
kuzdu

0

就我而言,直到我听取了Otziii在评论中的建议后,该方法才起作用。清理,重建(无需重新启动),重新运行该应用程序。我也不需要去做onActivityCreated就可以onCreateView了。

有一次我还犯了错误的布局错误,因此显然没有得到预期的控件。


我已经看到它发生在onActivityCreated
Jemshit Iskenderov

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之前找到-必须先使用findViewById在Java / kotlin代码中找到XML形式的元素,然后才可以对该视图或元素执行操作。

-这就是为什么您得到空指针执行的原因

**

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.