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

Posted

技术标签:

【中文标题】尝试访问 Kotlin 片段中的视图时出现 NullPointerException【英文标题】:NullPointerException when trying to access views in a Kotlin fragment 【发布时间】:2016-04-05 03:27:03 【问题描述】:

如何通过Fragments 使用 Kotlin android 扩展? 如果我在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
    

`

【问题讨论】:

如果你想在 onCreateView 中做,btn_K 也将是 rootView 上的一个属性。你可以做rootView.btn_K.setOnClickListener 感谢@Makotosan,您的回答对我有用。 清理、重建和重启 Android Studio 对我有用 @Otziii 这个帖子最初是在 2015 年写的。第一个答案有 259 票并被接受。我认为没有必要添加更多答案。 @Solidak 我最近遇到了这个问题,尝试了所有答案,唯一使它起作用的是我现在评论的内容。我在这个线程上有一个答案,但它只是被否决了,所以我把它改成了评论。似乎人们仍然有这个问题,没有人提到要清理并重新启动。 【参考方案1】:

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"); 

【讨论】:

成功了!!谢谢。只是一个快速的提示以供将来参考。我有另一个异常,我挖得更深一点,结果发现空引用异常来自对 UI 线程的异步回调,它会尝试访问合成属性,但当时它已经为空。确保使用 Safe Call 运算符 (?.) 或其他一些空安全运算符。它还有助于保持视图的类引用,而不是依赖于onViewCreated() 之外的合成属性 一个问题 - 它为 Activity 和 Fragment 生成不同的代码?如果我们使用另一个不包含getView() 的结构或者它不能调用findViewById(),有没有办法解决它?例如,教它哪个函数会返回我的布局? 如果您有视图,也可以像rootView.btn_K 一样访问它(而不仅仅是在片段中,这可以在任何地方完成) 有效!然而,它应该从 Kotlin 文档中得到更多的强调。直到这篇文章我才注意到这种方法。无论如何谢谢! 我总是在 onViewCreated 中使用它,但仍然在某些设备上(我从 Crashlytics 得到报告)它得到“不得为空”异常。景色就在那里。我充气正确的布局,它适用于我的设备。奇怪的是不能在随机设备上工作。【参考方案2】:

你调用这个btn_K 太快了,因为它返回一个空值并且给你空指针异常。

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

onActivityCreated()

        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener

【讨论】:

我只想指出,由于某些原因,这个答案对我有用,而接受的答案却没有。我的观点在onViewCreated 中为空,但随后在onActivityCreated 中定义。不知道为什么。【参考方案3】:

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

导入特定布局的所有小部件属性很方便 一口气:

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

【讨论】:

是的,只需使用val view = inflater.inflate() view.button.text = "caption" 这是最好的答案 - 肯定是最简洁的解决方案! @CacheMeOutside 不,因为它仍然是样板代码rootView.subView.doSomething。最好使用从onViewCreated开始的视图【参考方案5】:

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

【讨论】:

【参考方案6】:

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

  

【讨论】:

为什么?哪里有这方面的知识?为什么不改为onViewCreated【参考方案7】:

在我遵循 cmets 中 Otziii 的建议之前,没有任何效果。清理、重建(无需重新启动)、重新运行应用程序。我也不需要使用onActivityCreated,只需onCreateView 就可以了。

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

【讨论】:

我在onActivityCreated也看到了这种情况【参考方案8】:

将其添加到@Egor Neliuba 的答案中,是的,每当您调用没有引用的视图时,kotlinex 都会查找 rootView,并且由于您在片段中,并且片段没有 getView() 方法。因此它可能会抛出NullPointerException

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

如前所述,要么覆盖 onViewCreated()

或者如果你想在其他类中绑定视图(比如匿名),你可以简单地创建一个这样的扩展函数,

fun View.bindViews()...

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

【讨论】:

【参考方案9】:
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 将元素形式的 xml 查找到您的 java/kotlin 代码,然后只有您可以对该视图或元素执行操作。

-这就是你得到空指针执行的原因

**

【讨论】:

以上是关于尝试访问 Kotlin 片段中的视图时出现 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

访问片段的子视图时出现空指针异常

尝试从 Kotlin 中的另一个活动访问 EditText 时出现空指针异常

从自定义视图访问 RelativeLayout 时出现 NullPointerException

尝试创建 OkHttpClient 对象时出现 kotlin/TypeCastException

在使用ViewPager时尝试从其父活动修改片段时出现空指针异常

Android ViewBinding 有时会在绑定对象处返回 null,因此在尝试访问视图时出现空指针异常