"Недостатньо інформації для висновку параметра T" для Kotlin та Android


110

Я намагаюся повторити наступний ListView у своєму додатку Android за допомогою Kotlin: https://github.com/bidrohi/KotlinListView .

На жаль, я отримую помилку, яку я не в змозі самостійно вирішити. Ось мій код:

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

Макети точно такі, як тут: https://github.com/bidrohi/KotlinListView/tree/master/app/src/main/res/layout

Повне повідомлення про помилку, яке я отримую, це таке: Помилка: (92, 31) Висновок типу не вдалося: недостатньо інформації, щоб зробити висновок про параметр T у веселому findViewById (p0: Int): T! Вкажіть це чітко.

Буду вдячний за будь-яку допомогу, яку я можу отримати.


2
Ви можете спробувати змінити this.label = ... as TextViewдо this.label = row?.findViewById<TextView>і зробити аналогічно для val listView = ...? Будь ласка, дайте мені знати, чи це працює, щоб я міг зробити це правильною відповіддю у такому випадку.
Крістіан Брюггеманн

1
Який рядок викликає помилку?
воддан

Чи можете ви продемонструвати проблему на меншому прикладі?
воддан

@ ChristianBrüggemann Ось так : i.imgur.com/ZeWKjt5.png і це: i.imgur.com/Can7w0p.png ? З вашими правками зараз є ці помилки: i.imgur.com/qqPAjrL.png
Тімо

1
Спробуйте це this.label = row? .FindViewById <TextView> (R.id.label) як TextView
Альф Мох

Відповіді:


224

Ви повинні використовувати API рівня 26 (або вище). Ця версія змінила підпис View.findViewById()- дивіться тут https://developer.android.com/about/versions/oreo/android-8.0-changes#fvbi-signature

Тож у вашому випадку, коли результат findViewByIdнеоднозначний, вам потрібно надати тип:

1 / Зміна

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

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

2 / Зміни

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

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

Зауважте, що в 2 / акторський склад потрібен лише тому, що rowє нульовим. Якщо також label було зведено нанівець, або якщо ви зробили rowнечисленним, це не потрібно.


10
Ось, як вирішити цю проблему: Відкрийте діалогове вікно Замінити в шляху (Ctrl + Shift + R) і встановіть прапорець. Замінити findViewById\((.+?)\)\s+as\s+(.+)з findViewById<$2>\($1\)і запустити заміну всіх файлів. Це вирішило майже всі мої помилки.
Густав Карлссон

1
Чому в Java достатньо зробити висновок типу View за замовчуванням, а недостатньо в Kotlin? findViewById(R.id.tabLayout).setOnClickListener(v-> Log.d(TAG, "login: "));це добре для Java.
uTha9

findViewById\((.+?)\)\s+as\s+([A-Za-z0-9?]+)працює для мене краще. Це перешкоджає, щоб якийсь код рядка не закінчився @GustavKarlsson
user3680200

1
Посилання не говорить про це.
Елстон

7

Andoid O зміни findViewById api з

загальнодоступний Перегляд findViewById (int id);

до

загальнодоступний фінальний T findViewById (int id)

тому, якщо ви орієнтовані на API 26, ви можете змінити

val listView = findViewById (R.id.list) як ListView

до

val listView = findViewById (R.id.list)

або

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


4

Це працює

API рівня 25 або нижче використовують це

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

API рівня 26 або вище використовуйте це

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

Щасливе кодування!


2

Змініть свій код на це. Там, де відбулися основні зміни, позначаються зірочками.

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

Якщо ви орієнтуєтесь на API 26 у своєму додатку, відповідь нижче - правильна відповідь. val listView = findViewById<ListView>(R.id.list)
EricWasTaken

@ericWasTaken Так, ви праві. Компілятор повинен виводити тип із вмісту в дужках. Іноді це не вдається. Ваш код також правильний. Буде оновлено мою відповідь, щоб відобразити зміни.
Альф Мох

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.