NullPointerException เมื่อพยายามเข้าถึงมุมมองในส่วน Kotlin


240

จะใช้ Kotlin Android Extensions กับFragments ได้อย่างไร? ถ้าฉันใช้ภายในonCreateView()ฉันได้รับNullPointerExceptionข้อยกเว้นนี้:

เกิดจาก: java.lang.NullPointerException: พยายามเรียกวิธีเสมือน 'android.view.View android.view.View.f.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
    }
}
`

4
หากคุณต้องการทำใน onCreateView btn_K จะเป็นคุณสมบัติบน rootView เช่นกัน คุณสามารถทำได้rootView.btn_K.setOnClickListener
Makotosan

ขอบคุณ @Makotosan คำตอบของคุณใช้ได้สำหรับฉัน
Mahdi Bagjani

ทำความสะอาดสร้างและรีสตาร์ทสตูดิโอ Android ที่เหมาะกับฉัน
Otziii

@Otziii กระทู้นี้เขียนขึ้นครั้งแรกในปี 2015 คำตอบแรกมี 259 คะแนนโหวตและเป็นที่ยอมรับ ฉันไม่คิดว่าจำเป็นต้องเพิ่มคำตอบเพิ่มเติม
solidak

2
@Solidak ฉันมีปัญหานี้เมื่อเร็ว ๆ นี้ลองคำตอบทั้งหมดและสิ่งเดียวที่ทำให้มันทำงานได้คือสิ่งที่ฉันแสดงความคิดเห็นในขณะนี้ ฉันมีคำตอบในกระทู้นี้ แต่มันเพิ่งถูกลดระดับลงดังนั้นฉันจึงเปลี่ยนเป็นความคิดเห็น ดูเหมือนว่าคนยังคงมีปัญหานี้อยู่และไม่มีใครพูดถึงการทำความสะอาดและรีสตาร์ท
Otziii

คำตอบ:


443

คุณสมบัติสังเคราะห์ Kotlin ไม่ใช่เวทมนต์และทำงานในวิธีที่ง่ายมาก เมื่อคุณเข้าถึงมันเรียกร้องให้btn_KgetView().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 นั้นมาจากการโทรกลับ async ไปยังเธรด UI ซึ่งจะพยายามเข้าถึงคุณสมบัติสังเคราะห์ แต่มันก็เป็นโมฆะในเวลานั้น ตรวจสอบให้แน่ใจว่าใช้ตัวดำเนินการโทรปลอดภัย (?.) หรือตัวดำเนินการด้านความปลอดภัยอื่น ๆ นอกจากนี้ยังจะช่วยในการอ้างอิงชั้นเรียนของมุมมองและไม่พึ่งพาคุณสมบัติสังเคราะห์นอกonViewCreated()
solidak

2
แม้ว่าคำถามหนึ่ง - มันสร้างรหัสที่แตกต่างกันสำหรับกิจกรรมและชิ้นส่วน? หากเราใช้โครงสร้างอื่นที่ไม่มีgetView()หรือไม่สามารถเรียกใช้findViewById()มีวิธีแก้ไขหรือไม่ ตัวอย่างเช่นสอนให้ฟังก์ชั่นที่จะกลับมาจัดวางของฉัน?
milosmns

7
นอกจากนี้คุณยังสามารถเข้าถึงได้เช่นrootView.btn_Kถ้าคุณมีมุมมอง (และไม่เพียง แต่ในชิ้นส่วนนี้สามารถทำได้ทุกที่)
Adib Faramarzi

มันได้ผล ! อย่างไรก็ตามควรมีการขีดเส้นใต้เพิ่มเติมจากเอกสารของ Kotlin ฉันไม่ได้สังเกตวิธีนี้จนกระทั่งโพสต์นี้ .. ขอบคุณอยู่ดี!
sokarcreative

2
ฉันมักจะใช้มันใน onViewCreated แต่ยังคงอยู่ในอุปกรณ์บางอย่าง (ฉันได้รับรายงานจาก Crashlytics) มันได้รับข้อยกเว้น "ต้องไม่เป็นโมฆะ" มุมมองอยู่ที่นั่น ฉันขยายเค้าโครงที่ถูกต้องมันจะทำงานบนอุปกรณ์ของฉัน มันแปลกที่ไม่ได้ทำงานบนอุปกรณ์แบบสุ่ม
Arie Agung

9

คุณกำลังเรียกสิ่งนี้btn_Kเร็วเกินไปเมื่อถึงเวลานั้นมันจะคืนค่าว่างและให้ Null Pointer Exception แก่คุณ

คุณสามารถใช้มุมมองเหล่านี้โดยปลั๊กอินสังเคราะห์นี้ในonActivityCreated()วิธีที่เรียกว่าหลังจากonCreateView()วงจรชีวิตของ Fragment

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

ฉันแค่ต้องการชี้ให้เห็นว่าด้วยเหตุผลบางอย่างคำตอบนี้ใช้ได้สำหรับฉันในขณะที่คำตอบที่ยอมรับไม่ได้ มุมมองของฉันเป็นโมฆะในแต่ที่กำหนดไว้แล้วในonViewCreated onActivityCreatedไม่รู้ทำไม
NathanL

6

คุณสมบัติสังเคราะห์สร้างโดยปลั๊กอิน Kotlin Android ส่วนขยายความต้องการ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?และตรวจสอบการนำเข้าคุณสมบัติสังเคราะห์

มันสะดวกที่จะนำเข้าคุณสมบัติวิดเจ็ตทั้งหมดสำหรับรูปแบบที่เฉพาะเจาะจงในครั้งเดียว:

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

ไม่จำเป็นต้องกำหนดวัตถุที่เป็นคู่หูเพียงแค่เรียกทุก 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

มีสองวิธีที่จะเอาชนะสิ่งนี้

  • ไม่ว่าคุณจะแทนที่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 ก่อนค้นหา - คุณต้องค้นหาองค์ประกอบ xml ในรูปแบบจาวาโค้ดของคุณด้วยรหัส java / kotlin โดยใช้ findViewById จากนั้นมีเพียงคุณเท่านั้นที่สามารถดำเนินการกับมุมมองหรือองค์ประกอบนั้นได้

- นั่นคือสาเหตุที่การประมวลผลตัวชี้แบบ null คุณมี

**

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.