148

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.

Floern
  • 33,559
  • 24
  • 104
  • 119
Timo Güntner
  • 2,863
  • 4
  • 17
  • 24
  • 2
    Can you try changing `this.label = ... as TextView` to `this.label = row?.findViewById` 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. – Christian Brüggemann Jul 23 '17 at 16:04
  • 1
    Which line causes error? – voddan Jul 23 '17 at 16:37
  • Can you demonstrate the problem with a smaller example? – voddan Jul 23 '17 at 16:37
  • @ChristianBrüggemann Like this: http://i.imgur.com/ZeWKjt5.png and this: http://i.imgur.com/Can7w0p.png ? With your edits there are now these errors: http://i.imgur.com/qqPAjrL.png – Timo Güntner Jul 23 '17 at 17:37
  • @voddan This line causes the error: this.label = row?.findViewById(R.id.label) as TextView – Timo Güntner Jul 23 '17 at 17:39
  • 1
    Try this this.label = row?.findViewById(R.id.label) as TextView – Alf Moh Jul 23 '17 at 18:27
  • @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.ListView – Timo Güntner Jul 23 '17 at 18:53
  • Add a question mark to the end of TextView – Alf Moh Jul 23 '17 at 19:04
  • @AlfMoh With a question mark at the end (this.label = row?.findViewById(R.id.label) as TextView?) I get the following error: Error:(94, 26) Type mismatch: inferred type is TextView? but TextView was expected – Timo Güntner Jul 23 '17 at 19:24
  • what about this? public val label: TextView? init { this.label = row?.findViewById(R.id.label) as TextView? } – Alf Moh Jul 23 '17 at 19:25
  • 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** @AlfMoh – Timo Güntner Jul 23 '17 at 19:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149942/discussion-between-alf-moh-and-timo-guntner). – Alf Moh Jul 23 '17 at 19:32

7 Answers7

264

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.

nmw
  • 6,664
  • 3
  • 31
  • 32
  • 12
    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. – Gustav Karlsson Oct 07 '17 at 08:17
  • 1
    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. – MainActivity Jan 31 '18 at 17:09
  • ``findViewById\((.+?)\)\s+as\s+([A-Za-z0-9?]+)`` works better for me. It prevents some one line code is not over @GustavKarlsson – user3680200 Feb 24 '19 at 18:24
  • 1
    The link doesn't say that. – Alston Nov 09 '19 at 09:08
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)

Trinea
  • 649
  • 8
  • 10
7

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 !

Keshav Gera
  • 10,807
  • 1
  • 75
  • 53
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 }**
    }
}
Alf Moh
  • 7,159
  • 5
  • 41
  • 50
  • If you are targeting API 26 in your App, then the answer below is the correct answer. `val listView = findViewById(R.id.list)` – EricWasTaken Aug 02 '17 at 05:41
  • @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. – Alf Moh Aug 02 '17 at 05:49
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 ;)

pauminku
  • 3,526
  • 3
  • 22
  • 24
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")!!
Fazal Hussain
  • 1,129
  • 12
  • 28
Azade Rahmati
  • 135
  • 1
  • 5
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.

shivlal kumavat
  • 868
  • 1
  • 12
  • 28