"Not enough information to infer parameter T" with Kotlin and Android
Asked Answered
P

7

150

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.

Picofarad answered 23/7, 2017 at 15:53 Comment(12)
Can you try changing this.label = ... as TextView to this.label = row?.findViewById<TextView> and do so analogously for val listView = ...? Please let me know if this works so I can make this a proper answer in that case.Dotdotage
Which line causes error?Pm
Can you demonstrate the problem with a smaller example?Pm
@ChristianBrüggemann Like this: i.imgur.com/ZeWKjt5.png and this: i.imgur.com/Can7w0p.png ? With your edits there are now these errors: i.imgur.com/qqPAjrL.pngCopenhagen
@Pm This line causes the error: this.label = row?.findViewById(R.id.label) as TextViewCopenhagen
Try this this.label = row?.findViewById<TextView>(R.id.label) as TextViewOpenminded
@AlfMoh The app starts now but crashes immediately after that. The error in the debug console is now: kotlin.TypeCastException: null cannot be cast to non-null type android.widget.ListViewCopenhagen
Add a question mark to the end of TextViewOpenminded
@AlfMoh With a question mark at the end (this.label = row?.findViewById<TextView>(R.id.label) as TextView?) I get the following error: Error:(94, 26) Type mismatch: inferred type is TextView? but TextView was expectedCopenhagen
what about this? public val label: TextView? init { this.label = row?.findViewById<TextView?>(R.id.label) as TextView? }Openminded
Unfortunately same error: Error:(94, 26) Type mismatch: inferred type is TextView? but TextView was expected. And this in the Android Monitor: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.phenakit.tg.phenakit/com.phenakit.tg.phenakit.MainActivity}: kotlin.TypeCastException: null cannot be cast to non-null type android.widget.ListView @AlfMohCopenhagen
Let us continue this discussion in chat.Openminded
S
267

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.

Skirmish answered 25/7, 2017 at 8:16 Comment(4)
Here's how to batch-resolve this: Open the Replace in Path dialog (Ctrl+Shift+R) and check the regex box. Replace findViewById\((.+?)\)\s+as\s+(.+) with findViewById<$2>\($1\) and run the replace on all files. It solved almost all of my errors.Baldachin
Why it is sufficient in Java to infer View type by default and not sufficient in Kotlin? findViewById(R.id.tabLayout).setOnClickListener(v-> Log.d(TAG, "login: ")); this is OK for Java.Pinwheel
findViewById\((.+?)\)\s+as\s+([A-Za-z0-9?]+) works better for me. It prevents some one line code is not over @GustavKarlssonSolicitous
The link doesn't say that.Pleading
S
9

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)

Straub answered 3/8, 2017 at 15:34 Comment(0)
I
8

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 !

Interstadial answered 13/11, 2017 at 11:57 Comment(0)
O
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 }**
    }
}
Openminded answered 23/7, 2017 at 20:26 Comment(2)
If you are targeting API 26 in your App, then the answer below is the correct answer. val listView = findViewById<ListView>(R.id.list)Stirpiculture
@ericWasTaken Yeah, you are right. The compiler is supposed to infer the type from the contents in the brackets. Sometimes it fails at that. Your code is also correct. Will update my answer to reflect the changes.Openminded
S
1

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 ;)

Sarina answered 12/12, 2017 at 13:35 Comment(0)
P
1

In android 12 remove as from your code and put your model in <> in front of getParcelableExtra.

change

intent.getParcelableExtra("MyModel") as MyModel

to

intent.getParcelableExtra `<MyModel>` ("MyModel")!!
Passageway answered 26/1, 2022 at 10:50 Comment(0)
P
0

I came across this error while using the DataBindingUtil.setContentView(this,R.layout.activity_main)

The error came here because this is supposed to return the binding associated with the layout_main(inflatted layout) so I resolved this error by :

ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main)

So, your error may be mostly with handling the return value.Try to set that and check.

Pretended answered 6/6, 2022 at 5:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.