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

NullPointerException when trying to access views in a Kotlin fragment

如何将 Kotlin Android 扩展与 Fragment 一起使用? 如果我在 onCreateView() 中使用它们,我会得到这个 NullPointerException 异常:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference

这是片段代码:

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() returns null onCreateView。尝试在 onViewCreated 方法中进行:

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

您调用此 btn_K 太早了,因为当时它 returns 为 null 并给您 Null Pointer Exception。

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

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}
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 然后只有你可以对该视图或元素执行操作。

-这就是你得到空指针异常的原因

**

在 Fragments 中,请在 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()

  }

您唯一需要做的是:

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
}

Kotlin Android Extensions plugin 生成的综合属性需要 view 用于 Fragment/Activity 以预先设置。

在您的情况下,对于 Fragment,您需要在 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
}

或者更好的是,您应该只访问 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?,并检查 Importing synthetic properties

It is convenient to import all widget properties for a specific layout in one go:

import kotlinx.android.synthetic.main.<layout>.*

Thus if the layout filename is activity_main.xml, we'd import kotlinx.android.synthetic.main.activity_main.*.

If we want to call the synthetic properties on View, we should also import kotlinx.android.synthetic.main.activity_main.view.*.

在我的情况下,在我遵循评论中 Otziii 的建议之前,没有任何效果。清理、重建(无需重启)、重新 运行 应用程序。我也不需要使用 onActivityCreated,只需 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 都会查找 rootView,并且由于您在片段中并且片段没有 getView() 方法。因此它可能会抛出 NullPointerException

有两种方法可以解决这个问题,

  • 要么你重写 onViewCreated() 如前所述
  • 或者如果你想在其他class(比如匿名)中绑定视图,你可以简单地创建一个像这样的扩展函数,

    fun View.bindViews(){...}

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