skip to Main Content

Currently I am using a string array to hold some data that is used by my adapter, but I need to use the data in a listof:

Currently I am using this:

<string-array name="vegetables">
<item> Onion </item>
<item> Chillis </item>
<item> Tomatoes </item>
<item> Garlic Beans</item>

This is the data format I need to use:

val VegName  = listOf(
Veg(0, "Onion", R.drawable.onion),
Veg(1, "Chillis", R.drawable.chilli),
Veg(2, "Tomatoes (indoor)", R.drawable.tomatoe),
Veg(3, "Garlic", R.drawable.garlic) 

)

The adapter code:

class SearchActivity : AppCompatActivity() {
private lateinit var toolbar: Toolbar
lateinit var adapter: ArrayAdapter<*>
private lateinit var listView: ListView
private lateinit var emptyView: TextView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.search_main)
    title = "Search Veg"
    toolbar = findViewById(R.id.toolbar)
    listView = findViewById(R.id.listView)
    emptyView = findViewById(R.id.emptyView)
    adapter = ArrayAdapter<Any?>(this, android.R.layout.simple_list_item_1,
        resources.getStringArray(R.array.vegetables))
    adapter = ArrayAdapter<Any?>(this, android.R.layout.simple_list_item_1,
        resources.getStringArray(R.array.vegetables)) // I THINK THIS IS WHERE IT NEEDS CHANGING?
    listView.adapter = adapter
    listView.onItemClickListener = OnItemClickListener { adapterView, _, i, _ ->
        Toast.makeText(this@SearchActivity, adapterView.getItemAtPosition(i).toString(),
            Toast.LENGTH_SHORT).show()
    }
    listView.emptyView = emptyView
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.nav_menu, menu)
    menuInflater.inflate(R.menu.menu, menu)
    val search = menu.findItem(R.id.appSearchBar)
    val searchView : SearchView? = search?.actionView as SearchView?

    searchView?.queryHint = "Search"

    listView.setOnItemClickListener(OnItemClickListener { parent, view, position, id ->
        val intent = Intent()
        val theItemClicked = String()
        Toast.makeText(this@SearchActivity, listView.getItemAtPosition(position).toString(),
            Toast.LENGTH_SHORT).show()
        intent.putExtra("result",  listView.getItemAtPosition(position).toString() )
        intent.putExtra("position", position.toString())
        setResult(RESULT_OK, intent)
        finish()
    })
    searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String?): Boolean {
            return false
        }
        override fun onQueryTextChange(newText: String?): Boolean {
            adapter.filter.filter(newText)
            return true
        }
    })
    return super.onCreateOptionsMenu(menu)
}

}

Any help on changing the data format would be really appreciated!

Cheers

2

Answers


  1. You can build your list based on string-array data, and pass it to the adapter:

    val vegetables = context.resources.getStringArray(R.array.vegetables)
    val vegNames  = listOf(
        Veg(0, vegetables[0], R.drawable.onion),
        Veg(1, vegetables[1], R.drawable.chilli),
        Veg(2, vegetables[2], R.drawable.tomatoe),
        Veg(3, vegetables[3], R.drawable.garlic)
    )
    
    Login or Signup to reply.
  2. You can pull out the Array of Strings by doing resources.getStringArray(R.array.vegetables), but then you still have to associate those strings with your other data (like its drawable). You’d need to store that data in another structure, and linking it to a particular string or index in the array is brittle.

    If you can, I’d recommend adding the strings as separate resources, and referencing them in the array:

    
    <string name="onion">Onion</string>
    <string name="chillis">Chillis</string>
    <string name="tomatoes">Tomatoes</string>
    <string name="garlic_beans">Garlic Beans</string>
    
    <string-array name="vegetables">
        <item>@string/onion</item>
        <item>@string/chillis</item>
        <item>@string/tomatoes</item>
        <item>@string/garlic_beans</item>
    </string-array>
    

    Then your code can just reference those strings when you define each item and its related data:

    // The Veg class holds a string reference ID instead of an actual string
    val VegName  = listOf(
        Veg(0, R.string.onion, R.drawable.onion),
        Veg(1, R.string.chillis, R.drawable.chilli),
        Veg(2, R.string.tomatoes, R.drawable.tomatoe),
        Veg(3, R.string.garlic_beans, R.drawable.garlic)
    )
    

    and when you need to display that text, you can just context.getString(veg.stringId) with that ID property, same as how you’re providing a drawable ID when it needs to be displayed.

    If you don’t want to do that, or you can’t change the Veg class, you’ll have to define your data separately and then convert it (which you’d have to do if you were pulling from the string-array anyway). I like enums for this, I think it’s neat:

    enum class VegData(@StringRes stringResId: Int, @DrawableRes drawableResId: Int) {
        ONION(R.string.onion, R.drawable.onion),
        CHILLIS(R.string.chillis, R.drawable.chilli),
        TOMATOES(R.string.tomatoes, R.drawable.tomatoe),
        GARLIC_BEANS(R.string.garlic_beans, R.drawable.garlic)
    }
    
    // might want to cache this result the first time it's called, or maybe not
    // (e.g. if you want the strings to update with a language change)
    fun getVeg(context: Context) = VegData.values().mapIndexed { index, veg ->
        Veg(index, context.getString(veg.stringResId), veg.drawableResId)
    }
    

    That way you have one source of truth for the name of each thing, and everything that uses it can just reference that. You have the string array if you still need it, you have your Veg list, and they’re all just referencing the same string resources. If you ever update the strings it doesn’t matter, if you support multiple languages it just pulls the correct ones

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search