Kotlin 프래그먼트의 뷰에 액세스하려고 할 때 NullPointerException이 발생했습니다.


240

FragmentS 와 함께 Kotlin Android Extensions를 사용하는 방법은 무엇입니까? inside을 사용 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
Makotosan

감사합니다 @Makotosan 귀하의 답변이 저에게 효과적이었습니다.
Mahdi Bagjani

Android Studio를 정리, 재 구축 및 다시 시작했습니다.
Otziii

@Otziii이 스레드는 2015 년에 처음 작성되었습니다. 첫 번째 답변은 259 표로 승인되었습니다. 더 많은 답변을 추가해야한다고 생각하지 않습니다.
solidak

2
@ Solidak 나는 최근 에이 문제를 겪고 모든 답을 시도했으며 그것이 효과를 얻은 유일한 것은 내가 지금 언급 한 것입니다. 이 스레드에 대한 답변이 있었지만 방금 다운되었으므로 주석으로 변경했습니다. 사람들이 여전히이 문제를 겪고있는 것처럼 보이며 아무도 정리하고 다시 시작한다고 언급하지 않았습니다.
Otziii

답변:


443

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

2
효과가 있었다 !! 감사. 나중에 참조 할 수있는 빠른 준비입니다. 나는 또 다른 예외가 있었으며 조금 더 깊이 파고 들었고 Null Reference Exception이 비동기 콜백에서 합성 스레드에 액세스하려고 시도하는 UI 스레드로오고 있었지만 그 당시에는 이미 null이었습니다. 안전 통화 연산자 (?.) 또는 다른 null 안전 연산자 를 사용해야합니다 . 또한 뷰에 대한 클래스 참조를 유지하고 외부의 합성 속성에 의존하지 않아야합니다.onViewCreated()
solidak

2
한 가지 질문-Activity와 Fragment에 대해 다른 코드를 생성합니까? 포함하지 getView()않거나 호출 할 수없는 다른 구조를 사용하는 경우 이를 해결할 수 findViewById()있는 방법이 있습니까? 예를 들어 어떤 함수가 내 레이아웃을 반환하는지 알려주세요.
milosmns

7
당신은 또한 rootView.btn_K당신이 볼 수있는 것처럼 액세스 할 수 있습니다 (그리고 조각뿐만 아니라, 이것은 어디서나 수행 할 수 있습니다)
Adib Faramarzi

효과가있다 ! 그러나 Kotlin 설명서에서 더 밑줄이 있어야합니다. 이 게시물까지이 방법을 눈치 채지 못했습니다. 어쨌든 감사합니다!
sokarcreative

2
나는 항상 onViewCreated에서 사용했지만 여전히 일부 장치 (Crashlytics에서 보고서를 얻음)에서 "null이 아니어야 함"예외가 발생했습니다. 보기가 있습니다. 올바른 레이아웃을 부풀려 장치에서 작동합니다. 무작위 장치에서 작동하지 않는 것은 이상합니다.
Arie Agung

9

btn_K그 시점에서 null을 반환하고 널 포인터 예외를 발생시키는 즉시 호출합니다 .

Fragment 라이프 사이클 onActivityCreated()직후 onCreateView()에 호출 되는 메소드 에서이 합성 플러그인으로 이러한 뷰를 사용할 수 있습니다 .

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

나는 어떤 이유로 든,이 대답은 나에게 도움이되었지만 받아 들인 대답은 효과가 없다는 것을 지적하고 싶습니다. 내보기가 null onViewCreated이지만에 정의되어 onActivityCreated있습니다. 왜 그런지 모르겠다.
NathanL

6

에 의해 생성 된 합성 특성 코 틀린 안드로이드 확장 플러그인 필요 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매개 변수가 널 (NULL)이어야한다 Bundle?, 또한 확인 합성 속성을 가져 오기

특정 레이아웃에 대한 모든 위젯 속성을 한 번에 가져 오는 것이 편리합니다.

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

따라서 레이아웃 파일 이름이 activity_main.xml이면 가져옵니다. kotlinx.android.synthetic.main.activity_main.*.

View에서 합성 속성을 호출하려면 가져와야합니다. 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

이것이 가장 좋은 답변입니다-가장 확실한 솔루션입니다!
CacheMeOutside

여전히 상용구 코드이기 때문에 @CacheMeOutside 아니오 rootView.subView.doSomething. 다음에서 시작하는보기를 사용하는 것이 좋습니다.onViewCreated
user924

3

컴패니언 객체를 정의 할 필요가 없습니다.

 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

이것을 극복하는 두 가지 방법이 있습니다.

  • onViewCreated()언급 한대로 재정의하십시오.
  • 또는 다른 클래스 (예 : 익명)에서 뷰를 바인딩하려면 다음과 같이 확장 기능을 만들면됩니다.

    fun View.bindViews(){...}

두 번째 방법은 여러 동작을 가진 단일 조각이있을 때 유용합니다.


-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를 사용하여 java / kotlin 코드에서 xml 요소 양식을 찾아야 만 해당 뷰 또는 요소에 대한 조작을 수행 할 수 있습니다.

-그래서 널 포인터 실행이 발생하는 이유

**

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.