Android 尝试访问Kotlin片段中的视图时出现NullPointerException

Android 尝试访问Kotlin片段中的视图时出现NullPointerException,android,kotlin,kotlin-android-extensions,Android,Kotlin,Kotlin Android Extensions,如何将Kotlin Android扩展与Fragments一起使用? 如果我在onCreateView()中使用它们,我会得到以下NullPointerException异常: 原因:java.lang.NullPointerException:尝试调用虚拟机 方法“android.view.view android.view.view.findViewById(int)”在 空对象引用 以下是片段代码: package com.obaied.testrun.Fragment import a

如何将Kotlin Android扩展与
Fragment
s一起使用? 如果我在
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
    }
}
`

Kotlin的合成特性并不是神奇的,而是以一种非常简单的方式工作的。当您访问
btn_K
时,它调用
getView().findViewById(R.id.btn_K)

问题是您访问它的时间太短
getView()
onCreateView
中返回
null
。尝试在
onViewCreated
方法中执行此操作:

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

您调用此
btn_K
太快了,因为此时它返回null,并向您提供null指针异常

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

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}
class CardSelectorFragment:Fragment(){
val TAG=javaClass.canonicalName
伴星{
fun newInstance():CardSelectorFragment{
返回CardSelectorFragment()
}
}
覆盖创建视图(充气机:布局充气机?、容器:视图组?、savedInstanceState:捆绑?):视图{
var rootView=充气机?充气(R.layout.fragment\u card\u选择器,容器,错误)
rootView?.findViewById(R.id.mTextView)?.setOnClickListener{
d(标记“onViewCreated():helloworld”);
}
//btn_K.setOnClickListener{Log.d(标记,“onViewCreated():hello world”);}
返回根视图
}
}

**在查找之前,您正在使用btn_K.setOnClickListener -您必须使用findviewbyd找到从xml到java/kotlin代码的元素,然后只有您可以对该视图或元素执行操作

-所以这就是为什么你有空指针执行选项
**

请在OnActivity中编写代码创建:-

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()

  }

您唯一需要做的是:

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
}

由生成的合成属性需要一个
视图
,以便
片段/活动
事先设置

在您的情况下,对于
片段
,您需要在
onViewCreated
中使用
view.btn_K

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
}
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`
}
或者更好,您应该只在
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
}
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?
,并检查

可以方便地为特定布局导入所有小部件属性 一次性:

导入kotlinx.android.synthetic.main.*

因此,如果布局文件名是activity_main.xml,我们将导入
kotlinx.android.synthetic.main.activity\u main...

如果我们想在视图中调用合成属性,我们还应该 导入
kotlinx.android.synthetic.main.activity\u main.view...


在我的情况下,在我遵循评论中的建议之前,一切都不起作用。清理、重建(无需重新启动),重新运行应用程序。我也不需要在activitycreated上使用
,只要
onCreateView
就可以了


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

无需定义伴生对象,只需通过如下视图调用每个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
}

将它添加到@Egor Neliuba的答案中,无论何时调用没有引用的视图,kotlinex都会查找根视图,因为您在片段中,而片段没有
getView()
方法。因此,它可能会抛出
NullPointerException

有两种方法可以克服这个问题

  • 您可以覆盖前面提到的
    onViewCreated()
  • 或者,如果您想在其他类(比如匿名类)中绑定视图,只需创建如下扩展函数

    fun View.bindViews(){…}


第二种方法很有用,当您有一个具有多个行为的片段时。

它起作用了!!谢谢只是简单的提醒一下,以备将来参考。我有另一个异常,我深入挖掘了一下,发现Null引用异常来自于对UI线程的异步回调,它将尝试访问合成属性,但当时它已经为Null。确保使用安全呼叫运算符(?)或其他空安全运算符。它也有助于保持视图的类引用,而不依赖于
onViewCreated()
之外的合成属性,但有一个问题-它为活动和片段生成不同的代码?如果我们使用另一个不包含
getView()
的结构,或者它不能调用
findViewById()
,有没有办法解决这个问题?例如,教它哪个函数将返回我的布局?如果你有一个视图(不仅仅是片段,这可以在任何地方完成),你也可以像
rootView.btn_K
一样访问它,它可以工作!但是,Kotlin文档中应该更加强调这一点。直到这篇文章我才注意到这个方法。。无论如何谢谢你!我总是在onViewCreated中使用它,但在某些设备上(我从Crashlytics获得了报告),它得到了“不得为null”异常。景色就在那里。我充气正确的布局,它在我的设备上工作。不在随机设备上工作很奇怪。如果您想在onCreateView中工作,btn_K也将是rootView上的一个属性。你可以做
rootView.btn_K.setOnClickListener
谢谢@Makotosan你的回答对我有用。清理、重建并重新启动Android studio对我有用me@Otziii这篇文章最早是在2015年写的。第一个答案有259票,被接受。我不认为有必要添加更多的答案。@Solidak我最近遇到了这个问题,尝试了所有的答案,并且是唯一能让它起作用的东西