„Za mało informacji, aby wywnioskować parametr T” w Kotlin i Androidzie


111

Próbuję replikować następujący ListView w mojej aplikacji na Androida przy użyciu Kotlin: https://github.com/bidrohi/KotlinListView .

Niestety pojawia się błąd, którego nie mogę rozwiązać samodzielnie. Oto mój kod:

MainActivity.kt:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val listView = findViewById(R.id.list) as ListView
    listView.adapter = ListExampleAdapter(this)
}

private class ListExampleAdapter(context: Context) : BaseAdapter() {
    internal var sList = arrayOf("Eins", "Zwei", "Drei")
    private  val mInflator: LayoutInflater

    init {
        this.mInflator = LayoutInflater.from(context)
    }

    override fun getCount(): Int {
        return sList.size
    }

    override fun getItem(position: Int): Any {
        return sList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
        val view: View?
        val vh: ListRowHolder

        if(convertView == null) {
            view = this.mInflator.inflate(R.layout.list_row, parent, false)
            vh = ListRowHolder(view)
            view.tag = vh
        } else {
            view = convertView
            vh = view.tag as ListRowHolder
        }

        vh.label.text = sList[position]
        return view
    }
}

private class ListRowHolder(row: View?) {
    public val label: TextView

    init {
        this.label = row?.findViewById(R.id.label) as TextView
    }
}
}

Układy są dokładnie takie, jak tutaj: https://github.com/bidrohi/KotlinListView/tree/master/app/src/main/res/layout

Pełny komunikat o błędzie, który otrzymuję, jest następujący: Błąd: (92, 31) Wnioskowanie o typie nie powiodło się: Za mało informacji, aby wywnioskować parametr T w fun findViewById (p0: Int): T! Określ to wyraźnie.

Byłbym wdzięczny za każdą pomoc, jaką mogę uzyskać.


2
Czy możesz spróbować zmienić this.label = ... as TextViewna this.label = row?.findViewById<TextView>i zrobić to analogicznie val listView = ...? Daj mi znać, czy to zadziała, więc w takim przypadku mogę podać poprawną odpowiedź.
Christian Brüggemann

1
Która linia powoduje błąd?
voddan

Czy możesz zademonstrować problem na mniejszym przykładzie?
voddan

@ ChristianBrüggemann Podoba mi się to: i.imgur.com/ZeWKjt5.png a to: i.imgur.com/Can7w0p.png ? Wraz z twoimi zmianami są teraz następujące błędy: i.imgur.com/qqPAjrL.png
Timo Güntner.

1
Wypróbuj this.label = row? .FindViewById <TextView> (R.id.label) jako TextView
Alf Moh

Odpowiedzi:


225

Musisz używać interfejsu API na poziomie 26 (lub wyższym). Ta wersja zmieniła sygnaturę View.findViewById()- patrz tutaj https://developer.android.com/about/versions/oreo/android-8.0-changes#fvbi-signature

Więc w twoim przypadku, gdy wynik findViewByIdjest niejednoznaczny, musisz podać typ:

1 / Zmień

val listView = findViewById(R.id.list) as ListView do

val listView = findViewById<ListView>(R.id.list)

2 / Zmień

this.label = row?.findViewById(R.id.label) as TextView do

this.label = row?.findViewById<TextView>(R.id.label) as TextView

Zauważ, że w 2 / rzutowanie jest wymagane tylko dlatego, że rowdopuszcza wartość null. Gdyby też label była dopuszczalna, lub jeśli rownie była dopuszczalna, nie byłaby wymagana.


10
Oto jak to rozwiązać wsadowo: Otwórz okno dialogowe Zamień w ścieżce (Ctrl + Shift + R) i zaznacz pole wyrażenia regularnego. Wymień findViewById\((.+?)\)\s+as\s+(.+)się findViewById<$2>\($1\)i uruchom wymienić na wszystkich plikach. To rozwiązało prawie wszystkie moje błędy.
Gustav Karlsson

1
Dlaczego w Javie wystarczy domyślny typ widoku, a niewystarczający w Kotlinie? findViewById(R.id.tabLayout).setOnClickListener(v-> Log.d(TAG, "login: "));to jest OK dla Java.
uTha9

findViewById\((.+?)\)\s+as\s+([A-Za-z0-9?]+)działa lepiej dla mnie. Zapobiega to, że jeden wiersz kodu się nie skończył @GustavKarlsson
user3680200

1
Link tego nie mówi.
Alston

7

Andoid O zmień api findViewById z

public Widok findViewById (int id);

do

public final T findViewById (int id)

więc jeśli jesteś celem API 26, możesz to zmienić

val listView = findViewById (R.id.list) jako ListView

do

val listView = findViewById (R.id.list)

lub

val listView: ListView = findViewById (R.id.list)


4

To działa

Użyj tego interfejsu API na poziomie 25 lub niższym

    var et_user_name = findViewById(R.id.et_user_name) as EditText

Użyj tego interfejsu API na poziomie 26 lub wyższym

    val et_user_name: EditText = findViewById(R.id.et_user_name)

Miłego kodowania!


2

Zmień swój kod na ten. Miejsca, w których zaszły główne zmiany, zaznaczono gwiazdkami.

package com.phenakit.tg.phenakit

import android.content.Context
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v7.app.AppCompatActivity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ListView
import android.widget.TextView

public class MainActivity : AppCompatActivity() {

    private var mTextMessage: TextView? = null

    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
        when (item.itemId) {
            R.id.navigation_home -> {
                mTextMessage!!.setText(R.string.title_home)
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_dashboard -> {
                mTextMessage!!.setText(R.string.title_dashboard)
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_notifications -> {
                setContentView(R.layout.activity_list_view)
                return@OnNavigationItemSelectedListener true
            }
        }
        false
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mTextMessage = findViewById(R.id.message) as TextView?
        val navigation = findViewById(R.id.navigation) as BottomNavigationView
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)


        **val listView = findViewById<ListView>(R.id.list)**



        **listView?.adapter = ListExampleAdapter(this)**
    }

    private class ListExampleAdapter(context: Context) : BaseAdapter() {
        internal var sList = arrayOf("Eins", "Zwei", "Drei")
        private  val mInflator: LayoutInflater

        init {
            this.mInflator = LayoutInflater.from(context)
        }

        override fun getCount(): Int {
            return sList.size
        }

        override fun getItem(position: Int): Any {
            return sList[position]
        }

        override fun getItemId(position: Int): Long {
            return position.toLong()
        }

        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
            val view: View?
            val vh: ListRowHolder

            if(convertView == null) {
                view = this.mInflator.inflate(R.layout.list_row, parent, false)
                vh = ListRowHolder(view)
                view.tag = vh
            } else {
                view = convertView
                vh = view.tag as ListRowHolder
            }

            vh.label.text = sList[position]
            return view
        }
    }

    private class ListRowHolder(row: View?) {
        public var label: TextView

        **init { this.label = row?.findViewById<TextView?>(R.id.label) as TextView }**
    }
}

Jeśli kierujesz swoją aplikację na API 26, to odpowiedź poniżej jest poprawna. val listView = findViewById<ListView>(R.id.list)
EricWasTaken

@ericWasTaken Yeah, masz rację. Kompilator powinien wywnioskować typ na podstawie zawartości w nawiasach. Czasami to zawodzi. Twój kod jest również poprawny. Zaktualizuję moją odpowiedź, aby odzwierciedlić zmiany.
Alf Moh

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.