"Not enough information to infer parameter T" with Kotlin and Android
I'm trying to replicate the following ListView in my Android app using Kotlin: https://github.com/bidrohi/KotlinListView.
Unfortunately I'm getting an error I'm unable to resolve myself. Here's my code:
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
}
}
}
The layouts are exactly as here: https://github.com/bidrohi/KotlinListView/tree/master/app/src/main/res/layout
The full error message I'm getting is this: Error:(92, 31) Type inference failed: Not enough information to infer parameter T in fun findViewById(p0: Int): T! Please specify it explicitly.
I'd appreciate any help I can get.
Solution 1:
You must be using API level 26 (or above). This version has changed the signature of View.findViewById()
- see here https://developer.android.com/about/versions/oreo/android-8.0-changes#fvbi-signature
So in your case, where the result of findViewById
is ambiguous, you need to supply the type:
1/ Change
val listView = findViewById(R.id.list) as ListView
to
val listView = findViewById<ListView>(R.id.list)
2/ Change
this.label = row?.findViewById(R.id.label) as TextView
to
this.label = row?.findViewById<TextView>(R.id.label) as TextView
Note that in 2/ the cast is only required because row
is nullable. If label
was nullable too, or if you made row
not nullable, it wouldn't be required.
Solution 2:
Andoid O change findViewById api from
public View findViewById(int id);
to
public final T findViewById(int id)
so, if you are target to API 26, you can change
val listView = findViewById(R.id.list) as ListView
to
val listView = findViewById(R.id.list)
or
val listView: ListView = findViewById(R.id.list)
Solution 3:
Its Working
API Level 25 or below use this
var et_user_name = findViewById(R.id.et_user_name) as EditText
API Level 26 or Above use this
val et_user_name: EditText = findViewById(R.id.et_user_name)
Happy Coding !
Solution 4:
Change your code to this. Where the main changes occurred are marked with asterisks.
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 }**
}
}
Solution 5:
I would suggest you to use synthetics
kotlin Android extension:
https://kotlinlang.org/docs/tutorials/android-plugin.html
https://antonioleiva.com/kotlin-android-extensions/
In your case the code will be something like this:
init {
this.label = row.label
}
As simple as that ;)