Home > Software design >  Kotlin Change ViewText with an ID provided by a String
Kotlin Change ViewText with an ID provided by a String

Time:07-02

Goal: To get a ViewText resource and edit it from an activity, using a mutable string (because then the string can be changed to alter other ViewTexts in the same function).

Context: I'm making a grid using TableRows and TextViews that can be altered to form a sort of map that can be generated from an array.

Issue: The binding command does not recognise strings. See my comment "PROBLEM HERE".

Tried: getResources.getIdentifier but I've been told that reduces performance drastically.


An excerpt from gridmap.xml

<TextView
    android:id="@ id/cell1"/>

GridMap.kt

package com.example.arandomadventure

import android.R
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.arandomadventure.databinding.GridmapBinding

class GridMap : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //sets the binding and assigns it to view
        val binding: GridmapBinding = GridmapBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
    
        //creates a string variable
        var cellID = "cell1"
        //uses binding to set the background colour to teal
        binding.cellID.setBackgroundResource(R.color.teal_200) //<- PROBLEM HERE (Unresolved Reference)
        //getResources.getIdentifier is not an option as it degrades performance on a larger scale
    }
}

CodePudding user response:

A binding object is just an autogenerated class, whose class members are defined by the views in your layout XML. You can't add or access a field on a class with the syntax you showed - binding classes are no different from any other class. If you wanted to be able to access them by name, you could load them into a map

val viewMap = mapOf(
    "cell1" to binding.cell1,
    "cell2" to binding.cell2,
    "cell3" to binding.cell3
)

then you can use the map to access them by name

var cellID = "cell1"
viewMap[cellID].setBackgroundResource(R.color.teal_200)

If you want the map to be a class member, you can set it like this

private lateinit var viewMap: Map<String,View>

override fun onCreate(savedInstanceState: Bundle?) {
    //...
    viewMap = mapOf(
        "cell1" to binding.cell1,
        "cell2" to binding.cell2,
        "cell3" to binding.cell3
    )
}

If your layout has hundreds of views and this becomes cumbersome, you may want to consider adding the views programmatically instead.

Edit

If you want to do this a more ugly, but more automatic way you can use reflection. To do this you need to add this gradle dependency:

implementation "org.jetbrains.kotlin:kotlin-reflect:1.7.0"

then you can build up the map programmatically with all views in the binding.

val viewMap = mutableMapOf<String,View>()

ActivityMainBinding::class.members.forEach {
    try {
        val view = it.call(binding) as? View
        view?.let { v ->
            viewMap[it.name] = v
        }
    }
    catch(e: Exception) {
        // skip things that can't be called
    }
}
  • Related