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

时间:2015-12-31 05:41:43

标签: android kotlin kotlin-android-extensions

如何将Kotlin Android Extensions与Fragment一起使用? 如果我在onCreateView()中使用它们,我会收到NullPointerException例外:

  

引起:java.lang.NullPointerException:尝试调用虚拟   方法' android.view.View android.view.View.findViewById(int)'在...上   null对象引用

这是片段代码:

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
    }
}
`

10 个答案:

答案 0 :(得分:343)

Kotlin合成属性不是神奇的,并且以非常简单的方式工作。当您访问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"); }
}

答案 1 :(得分:6)

你过早地调用这个btn_K,因为那时它返回一个null并且给你Null Pointer Exception。

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

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

答案 2 :(得分:5)

Kotlin Android Extensions plugin生成的合成属性需要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?,并检查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.*.

答案 3 :(得分: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
}

答案 4 :(得分:3)

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

changeLanguage = (language) => {
    window.localStorage.setItem("siteLanguage", language);
    this.setState({})
    window.location.reload(1);
}
readTextFile(`/app/localization/localization.en.json`, 
(localizationContent) => {
        let localization = JSON.parse(localizationContent);
        this.props.setLocalizationMessages(localization.messages, 'en');
    });
    if (localStorage.getItem("siteLanguage") == "th") {
        readTextFile(`/app/localization/localization.th.json`, 
(localizationContent) => {
            let localization = JSON.parse(localizationContent);
            this.props.setLocalizationMessages(localization.messages, 
'th');
        });
    }

答案 5 :(得分: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()

  }

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

科特林的合成特性不是魔术,而是以非常简单的方式起作用的。当您访问btn_K时,它会调用getView().findViewById(R.id.btn_K)

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    if (mRootView == null) {
        mRootView = inflater.inflate(R.layout.fragment_profile, container, false)
    }

    return mRootView
}

如果您使用的是接口回调,那么您将得到NPE cuz,此时视图将不可用->您应该覆盖getView()方法

override fun getView(): View? {
    return mRootView
}

并始终在onViewCreated()

中使用您的视图
if (mCreatedView == null) {
   mCreatedView = view
   btn_K.setOnClickListener{
        //------------do something
   }
 }

答案 8 :(得分:0)

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

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

  • 您要么如上所述都覆盖onViewCreated()
  • 或者,如果您想将视图绑定到其他类(例如匿名)中,则只需创建一个扩展函数即可,

    fun View.bindViews(){...}

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

答案 9 :(得分:-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找到xml到你的java / kotlin代码的元素,然后才能对该视图或元素执行操作。

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

**