NullPulumException khi cố gắng truy cập vào các khung nhìn trong một đoạn Kotlin


238

Làm cách nào để sử dụng Tiện ích mở rộng Android của Kotlin với Fragments? Nếu tôi sử dụng chúng bên trong onCreateView(), tôi nhận được NullPointerExceptionngoại lệ này :

Nguyên nhân do: java.lang.NullPulumException: Cố gắng gọi phương thức ảo 'android.view.View android.view.View.findViewById (int)' trên tham chiếu đối tượng null

Đây là đoạn mã:

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
Nếu bạn muốn làm điều đó trong onCreateView, btn_K cũng sẽ là một thuộc tính trên rootView. Bạn có thể làmrootView.btn_K.setOnClickListener
Makotosan

Cảm ơn @Makotosan câu trả lời của bạn đã làm việc cho tôi.
Mahdi Bag camera

Dọn dẹp, xây dựng lại và khởi động lại studio Android làm việc cho tôi
Otziii

@Otziii Chủ đề này được viết lần đầu tiên vào năm 2015. Câu trả lời đầu tiên có 259 phiếu và được chấp nhận. Tôi không nghĩ cần thiết phải thêm câu trả lời.
solidak

2
@Solidak Tôi đã có vấn đề này gần đây, đã thử tất cả các câu trả lời và điều duy nhất làm cho nó hoạt động là những gì tôi nhận xét bây giờ. Tôi đã có một câu trả lời về chủ đề này, nhưng nó đã bị bỏ qua, vì vậy tôi đã thay đổi nó thành một nhận xét. Có vẻ như mọi người vẫn đang gặp phải vấn đề này, và không ai đề cập đến việc dọn dẹp và khởi động lại.
Otziii

Câu trả lời:


441

Thuộc tính tổng hợp của Kotlin không phải là phép thuật và hoạt động theo cách rất đơn giản. Khi bạn truy cập btn_K, nó gọi cho getView().findViewById(R.id.btn_K).

Vấn đề là bạn đang truy cập nó quá sớm. getView()trả lại nulltrong onCreateView. Hãy thử làm theo onViewCreatedphương pháp:

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

2
Nó đã làm việc!! Cảm ơn. Chỉ cần một cái đầu nhanh lên để tham khảo trong tương lai. Tôi có một ngoại lệ khác, và tôi đã đào sâu hơn một chút và hóa ra Null Reference Exception xuất phát từ một cuộc gọi lại không đồng bộ đến luồng UI nơi nó sẽ cố gắng truy cập vào thuộc tính tổng hợp, nhưng nó đã bị vô hiệu hóa vào thời điểm đó. Đảm bảo sử dụng toán tử Gọi an toàn (?.) Hoặc một số toán tử an toàn null khác. Nó cũng sẽ giúp giữ một tham chiếu lớp của chế độ xem và không dựa vào các thuộc tính tổng hợp bên ngoàionViewCreated()
solidak

2
Một câu hỏi mặc dù - nó tạo ra các mã khác nhau cho Activity và Fragment? Nếu chúng ta sử dụng một cấu trúc khác không chứa getView()hoặc nó không thể gọi findViewById(), có cách nào để khắc phục nó không? Ví dụ, dạy cho nó chức năng nào sẽ trả về bố cục của tôi?
milosmns

7
Bạn cũng có thể truy cập nó như rootView.btn_Knếu bạn có chế độ xem (và không chỉ trong các đoạn, điều này có thể được thực hiện ở mọi nơi)
Adib Faramarzi

Nó hoạt động! Tuy nhiên, nó nên được gạch chân nhiều hơn từ tài liệu của Kotlin. Tôi đã không nhận thấy phương pháp này cho đến bài viết này .. Dù sao cũng cảm ơn!
sokarcreative

2
Tôi luôn sử dụng nó trong onViewCreated, nhưng vẫn còn trên một số thiết bị (tôi đã nhận được báo cáo từ Crashlytics) nó có ngoại lệ "không được null". Quan điểm là ở đó. Tôi thổi phồng bố cục chính xác, nó hoạt động trên thiết bị của tôi. Nó chỉ lạ không hoạt động trên thiết bị ngẫu nhiên.
Arie Agung

9

Bạn đang gọi điều này btn_Kquá sớm vì tại thời điểm đó, nó trả về một giá trị null và cung cấp cho bạn Ngoại lệ con trỏ Null.

Bạn có thể sử dụng các chế độ xem này bằng plugin tổng hợp này trong onActivityCreated()phương thức được gọi ngay sau onCreateView()vòng đời của Fragment.

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

Tôi chỉ muốn chỉ ra rằng vì một số lý do, câu trả lời này có hiệu quả với tôi trong khi câu trả lời được chấp nhận thì không. Quan điểm của tôi là null trong onViewCreatednhưng sau đó được xác định trong onActivityCreated. Không biết tại sao mặc dù.
NathanL

6

Tính chất tổng hợp được tạo ra bởi Kotlin Android Extensions Plugin cần viewcho Fragment/Activityđược đặt trước mặt.

Trong trường hợp của bạn, cho Fragment, bạn cần sử dụng view.btn_KtrongonViewCreated

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
}

Hoặc tốt hơn, bạn chỉ nên truy cập các thuộc tính tổng hợp trong 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`
}

Xin lưu ý rằng savedInstanceStatetham số này phải là null Bundle?và cũng kiểm tra Nhập thuộc tính tổng hợp

Thật thuận tiện để nhập tất cả các thuộc tính widget cho một bố cục cụ thể trong một lần:

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

Do đó, nếu tên tệp bố cục là Activity_main.xml, chúng tôi sẽ nhập kotlinx.android.synthetic.main.activity_main.*.

Nếu chúng ta muốn gọi các thuộc tính tổng hợp trên View, chúng ta cũng nên nhập kotlinx.android.synthetic.main.activity_main.view.*.


3

điều duy nhất bạn cần làm là:

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
Có, chỉ cần sử dụng val view = inflater.inflate() view.button.text = "caption".
CoolMind 17/07/18

Đây là câu trả lời tốt nhất - giải pháp gọn gàng nhất cho chắc chắn!
CacheMeOutside

@CacheMeOutside không, bởi vì nó vẫn là mã soạn sẵn rootView.subView.doSomething. Tốt hơn là sử dụng các chế độ xem bắt đầu từonViewCreated
user924

3

không cần xác định đối tượng đồng hành, chỉ cần gọi mỗi id bằng một khung nhìn như

 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

Trong Fragment, vui lòng viết mã của bạn vào 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
Tại sao? Nơi nào có kiến ​​thức này? Tại sao không onViewCreatedthay thế?
kuzdu

0

Trong trường hợp của tôi, không có gì hiệu quả cho đến khi tôi làm theo lời khuyên từ Otziii trong các bình luận. Làm sạch, xây dựng lại (không cần khởi động lại), chạy lại ứng dụng. Tôi cũng không cần phải đi cùng onActivityCreatedvà chỉ cần onCreateViewthực hiện các mẹo.

Có lần tôi cũng mắc lỗi lạm phát bố cục sai, do đó không nhận được các điều khiển dự kiến ​​rõ ràng.


tôi đã thấy nó xảy ra trong onActivityCreatedquá
Jemshit Iskenderov

0

Thêm nó vào câu trả lời của @Egor Neliuba, Có bất cứ khi nào bạn gọi chế độ xem mà không cần tham chiếu, kotlinex sẽ tìm rootView và vì bạn đang ở trong một đoạn và đoạn không có getView() phương thức. Vì vậy, nó có thể némNullPointerException

Có hai cách để khắc phục điều này,

  • Hoặc bạn ghi đè onViewCreated() như đã đề cập
  • Hoặc Nếu bạn muốn liên kết các khung nhìn trong một số lớp khác (nói ẩn danh), bạn chỉ cần tạo một hàm mở rộng như thế này,

    fun View.bindViews(){...}

Cách tiếp cận thứ hai rất hữu ích, khi bạn có một đoạn duy nhất với nhiều hành vi.


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

}

** Ở đây bạn đang sử dụng btn_K.setOnClickListener trước khi tìm -Bạn phải tìm mẫu phần tử xml cho mã java / kotlin của mình bằng cách sử dụng findViewById và sau đó chỉ bạn mới có thể thực hiện thao tác trên chế độ xem hoặc phần tử đó.

-Vậy đó là lý do tại sao con trỏ null thực thi bạn có

**

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.