Search code examples
androidkotlinandroid-activity

ListView & Custom Adapter dont work Kotlin


I don’t understand why the list is not filled with the help of a custom adapter. Only the last element from the array gets into textView (textHeroView). And it turns out that the ListView does not go as a list, but simply with one item from the array. I tried to put it on a separate list, to no avail. Can you please tell me what I made a mistake?

HeroesAdapter

class HeroesAdapter(context: Context, heroes: List<TestHero>): BaseAdapter() {
    private val context = context
    private val heroes = heroes

    override fun getCount(): Int {
        return heroes.count()
    }

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

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

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

           // categoryView = LayoutInflater.from(context).inflate(R.layout.activity_heroes, null)
        val listheroView = LayoutInflater.from(context).inflate(R.layout.list_hero_view, parent, false)
           // val categoryImage: ImageView = categoryView.findViewById(R.id.heroesImageView)
            val heroText: TextView = listheroView.findViewById(R.id.textHeroView)
            val category = heroes[position]

           // heroText.text = category.legends.all.keys.first()
        for((key) in category.legends.all){
            Log.d("HeroesActivity", key)
            heroText.text = key
        }

            return listheroView

    }
}

list_hero_view

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ImageView
        android:id="@+id/heroesImageView"
        android:layout_width="426dp"
        android:layout_height="180dp"
        android:layout_marginTop="24dp"
        android:scaleType="centerCrop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.533"
        app:layout_constraintStart_toStartOf="parent"
        app:srcCompat="@mipmap/wraith_apex_legends" />

    <TextView
        android:id="@+id/textHeroView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Wraith"
        android:textColor="@android:color/darker_gray"
        android:textSize="54sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="@+id/heroesImageView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.12"
        app:layout_constraintStart_toStartOf="@+id/heroesImageView"
        app:layout_constraintTop_toTopOf="@+id/heroesImageView"
        app:layout_constraintVertical_bias="0.04000002" />
</androidx.constraintlayout.widget.ConstraintLayout>

activity_heroes

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HeroesActivity">

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginBottom="24dp"
        app:layout_constraintBottom_toTopOf="@+id/heroesListView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher_round" />

    <ListView
        android:id="@+id/heroesListView"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="100dp"
        android:layout_marginEnd="1dp"
        android:layout_marginBottom="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textNickname"
        android:layout_width="251dp"
        android:layout_height="29dp"
        android:layout_marginStart="40dp"
        android:layout_marginEnd="279dp"
        android:layout_marginBottom="60dp"
        android:text="Nickname"
        android:textSize="20sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/heroesListView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.145"
        app:layout_constraintStart_toEndOf="@+id/imageView2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.272" />

    <TextView
        android:id="@+id/textLvl"
        android:layout_width="111dp"
        android:layout_height="19dp"
        android:layout_marginEnd="28dp"
        android:layout_marginBottom="42dp"
        android:text="Lvl"
        app:layout_constraintBottom_toTopOf="@+id/heroesListView"
        app:layout_constraintEnd_toEndOf="@+id/textNickname"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/textNickname"
        app:layout_constraintTop_toBottomOf="@+id/textNickname"
        app:layout_constraintVertical_bias="0.562" />
</androidx.constraintlayout.widget.ConstraintLayout> 

HeroesActivity

class HeroesActivity : AppCompatActivity() {

    lateinit var heroesAdapt : HeroesAdapter

    val listHero = ArrayList<TestHero>()
    private val TAG = "HeroesActivity"

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

        getCurrentData()

        val lisView : ListView = findViewById(R.id.heroesListView)

        heroesAdapt = HeroesAdapter(this, listHero)
        lisView.adapter = heroesAdapt
        //heroesListView.adapter = heroesAdapt
        
            // heroesAdapt = HeroesAdapter(this, arrayListOf("a","b","c","d","e","f"));
        // lisView.adapter = heroesAdapt


    }

    private fun getCurrentData() {
        val api = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiRequest::class.java)

        GlobalScope.launch(Dispatchers.IO) {
            val response = api.herList().awaitResponse()
            if (response.isSuccessful) {
                val data = response.body()!!
                Log.d(TAG, data.toString())

                withContext(Dispatchers.Main){
                   // adapter.add(data.global.name)
                   // adapt.add(data.global.platform)

                    listHero.add(data)
                    heroesAdapt.notifyDataSetChanged()
                    textNickname.text = "${data.global.name} (${data.global.rank.rankDiv} divisions)"
                    textLvl.text = "Level: ${data.global.level.toString()}"
                    when(data.global.rank.rankName){
                        "Silver" -> textNickname.setTextColor(Color.parseColor("#7A7A79"))
                        "Gold" ->   textNickname.setTextColor(Color.parseColor("#E6D600"))
                        "Platinum" -> textNickname.setTextColor(Color.parseColor("#36BBCE"))
                    }

                }

            }
        }
    }
}

I think the problem is in this piece of code

    for((key) in category.legends.all){
        Log.d("HeroesActivity", key)
        heroText.text = key
    }

enter image description here

And logs screen

logs logs screen

TestHero.kt

data class TestHero (@SerializedName("global") val global: PlayerInf,
                     @SerializedName("legends")val legends: AllLegends)

data class  PlayerInf (val name: String, val uid: Long, val avatar: String, val platform: String,
val  level: Int, val toNextLevelPercent: Int, val internalUpdateCount: Int, val bans: BanInf, val rank: RankInf)

data class BanInf (val isActive: Boolean, val remainingSeconds: Int)

data class RankInf (val rankScore: Int, val rankName: String, val rankDiv: Int, val rankImg: String)


data class AllLegends (@SerializedName("all") val all: Map<String, LegendWrapper> = emptyMap())


data class LegendWrapper(
    val data: List<PlayerPerformance>? = emptyList()

)
data class PlayerPerformance(val name: String, val value: Int, val key: String)

Solution

  • To the adapter, it's one item for one view. Your backing List has only one item. Your for loop inside getView() is iterating some collection inside that single item, and setting the value of the same TextView over and over until it gets to the last one.

    Since the backing type of your adapter is TestHero, your list view can only show one line item per instance of TestHero. You only have one instance of TestHero.

    Based on what you're showing, you should make the backing type of your adapter whatever the type of the list TestHero.legends.all is. In your coroutine, you can pull that list out of your TestHero and pass it to your Adapter. You'll need to make the backing list property mutable.

    And like I said in the comments, don't use mutable lists for this. You should start with an empty list.

    For example, if the type of this list is Legend, your adapter should look something like:

    class HeroesAdapter(): BaseAdapter() {
        var heroes: List<Legend> = emptyList()
    
        override fun getCount(): Int = heroes.size
    
        override fun getItem(position: Int): = heroes[position]
    
        override fun getItemId(position: Int) = position
    
        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
            val view = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.list_hero_view, parent, false)
            val heroText: TextView = view.findViewById(R.id.textHeroView)
            val hero = heroes[position]
            heroText.text = hero.keys.first()
            return view
        }
    }
    

    And in your Activity, get rid of the listHero property. In your coroutine, after you get your data, do

    heroesAdapt.apply {
        heroes = data.legends.all
        notifyDataSetChanged()
    }
    

    Also, consider switching to RecyclerView. ListView is kind of obsolete and I keep expecting them to deprecate it because they have only worked on improving RecyclerView the past several years.