KotlinフラグメントのビューにアクセスしようとするとNullPointerException


240

FragmentsでKotlin Android拡張機能を使用する方法 内部onCreateView()で使用すると、次のNullPointerException例外が発生します。

原因:java.lang.NullPointerException:nullオブジェクト参照で仮想メソッド '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
誠さん

@誠さん、ありがとうございます。
Mahdi Bagjani

Androidスタジオのクリーン、再構築、再起動が
うまくいっ

@Otziiiこのスレッドは2015年に最初に書かれました。最初の回答は259票で、承認されました。答えを追加する必要はないと思います。
solidak

2
@Solidak私は最近この問題を抱えており、すべての答えを試しましたが、それを機能させる唯一のことは、今私がコメントしたことです。このスレッドで回答がありましたが、反対票が出たため、コメントに変更しました。人々はまだこの問題を抱えているようであり、誰もクリーンアップして再起動することについて言及していません。
Otziii 2018年

回答:


443

Kotlin合成プロパティは魔法ではなく、非常に単純な方法で機能します。にアクセスするとbtn_K、が必要になりますgetView().findViewById(R.id.btn_K)

問題は、アクセスが早すぎるということです。getView()リターンnullの中でonCreateViewonViewCreatedメソッドでそれをやってみてください:

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

2
出来た!!ありがとう。後で参照できるように、すぐにお知らせします。別の例外があり、もう少し深く掘り下げたところ、null参照例外は非同期コールバックからUIスレッドに送られ、そこで合成プロパティにアクセスしようとしましたが、その時点ではすでにnullでした。Safe Call演算子(?。)またはその他のnull安全演算子を必ず使用してください。また、ビューのクラス参照を保持し、外部の合成プロパティに依存しないことも役立ちますonViewCreated()
solidak

2
ただし、1つの質問-アクティビティとフラグメントに対して異なるコードが生成されますか?を含まないgetView()か呼び出せない別の構造を使用する場合、それをfindViewById()回避する方法はありますか?たとえば、レイアウトを返す関数を教えてください。
milosmns 2016年

7
rootView.btn_Kビューがある場合と同様にアクセスできます(断片化されているだけでなく、どこでも実行できます)
Adib Faramarzi

できます !ただし、Kotlinのドキュメントからはもっと下線が引かれているはずです。私はこの投稿までこの方法に気づきませんでした。とにかくありがとう!
sokarcreative

2
私は常にonViewCreatedでそれを使用しましたが、それでも一部のデバイス(Crashlyticsからレポートを取得しました)では、「nullであってはならない」例外が発生しました。ビューがあります。私は正しいレイアウトを膨らませます、それは私のデバイスで動作します。ランダムなデバイスで動作しないのは奇妙なことです。
Arie Agung

9

btn_Kその時点でnullを返し、nullポインター例外が発生しているため、これを呼び出すのが早すぎます。

これらのビューonActivityCreated()onCreateView()、フラグメントのライフサイクルの直後に呼び出されるメソッドで、この合成プラグインによって使用できます。

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

いくつかの理由で、この答えは私にとってはうまくいったが、受け入れられた答えはうまくいかなかったことを指摘したいだけです。ビューはnullですonViewCreatedが、で定義されていonActivityCreatedます。なぜだかわかりません。
NathanL 2018年

6

生成された合成プロパティKotlin Androidの拡張プラグインが必要viewのため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パラメータはnull Bundle?にできる必要があることに注意してください。また、合成プロパティのインポートも確認してください。

特定のレイアウトのすべてのウィジェットプロパティを一度にインポートすると便利です。

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

したがって、レイアウトファイル名がactivity_main.xmlの場合、インポートします kotlinx.android.synthetic.main.activity_main.*.

ビューで合成プロパティを呼び出す場合は、インポートする必要があります 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 2018

これが最良の答えです-最も確かな解決策です!
CacheMeOutside

@CacheMeOutsideいいえ、それはまだ定型コードであるためですrootView.subView.doSomething。次から開始するビューを使用することをおonViewCreated
勧めし

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

これを克服するには2つの方法があります。

  • onViewCreated()言及したようにオーバーライドするか
  • または、他のクラス(匿名など)でビューをバインドする場合は、次のような拡張関数を作成するだけです。

    fun View.bindViews(){...}

2番目のアプローチは、複数の動作を持つ単一のフラグメントがある場合に役立ちます。


-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コードに検索する必要があります。

-だからこそ、nullポインタを実行できます

**

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.