Home > OS >  Looking for a method to iterate through ImageViews in an activity with kotlin
Looking for a method to iterate through ImageViews in an activity with kotlin

Time:10-20

I have an activity with 12 Image views in a 3x4 matrix. I want to replace the image and other properties of each ImageView with different resources programmatically. I can't seem to find any way to iterate through the views with a loop so am having to hard code each change. I managed to get the ImageViews into an array which helps but I am hoping there is a better way. I looked at KotlinX.Synthetic and data/view binding but they don't seem to help. E.g to create the ImageView array I have had to do this:

 var imgArray = arrayOf(findViewById<ImageView>(R.id.img01))
 imgArray  = findViewById<ImageView>(R.id.img02)
 imgArray  = findViewById<ImageView>(R.id.img03)
 imgArray  = findViewById<ImageView>(R.id.img04)
 imgArray  = findViewById<ImageView>(R.id.img05)
 imgArray  = findViewById<ImageView>(R.id.img06)
 imgArray  = findViewById<ImageView>(R.id.img07)
 imgArray  = findViewById<ImageView>(R.id.img08)
 imgArray  = findViewById<ImageView>(R.id.img09)
 imgArray  = findViewById<ImageView>(R.id.img10)
 imgArray  = findViewById<ImageView>(R.id.img11)
 imgArray  = findViewById<ImageView>(R.id.img12)

I was hoping to do something like:

var i = 1
for (listView in listViews) {
    imgArray  = findViewById<ImageView>("R.id.img"   i.tostring())
i  
}

But that oviously wont work. Any help would be much appreciated. I have some experience with other languages but new to kotlin/java/android.

CodePudding user response:

The way I usually go is

val imageViews = listOf(R.id.img1, R.id.img2, R.id.img3).map(findViewById(it)) 

that will build you a list of views in one line.

Or there is resources.getIdentifier("nameOfDrawable", "drawable", this.getPackageName()) to get an id for a given name, if you really don't want to keep the list up to date and there is no variation in names.

CodePudding user response:

I usually go with the first example in Gabe's answer, just listing the IDs and using that to create useful collections you can iterate over. But here are a couple of other options that might be more suitable depending on the situation:

Find all views of a given type

container.children.filterIsInstance<ImageView>()

This takes a reference to a ViewGroup (e.g. a ConstraintLayout, GridLayout etc.) and basically pulls out all the Views of a certain type. Useful if they're organised in some kind of container layout, not so much if everything's on the same level (like a ConstraintLayout encourages you to do) and there are other Views of the same type you don't want included.

Also, this only works on the direct descendants of the container, the stuff which has it as a parent. If any of that stuff has its own descendants, they aren't included (see the function below though!)

Use tags

Tags are a bit of data you can add to a View, in XML with the tag attribute, or in code with setTag. You could use this to identify which ImageViews you want, if organising them in the hierarchy isn't possible. (The hierarchy is better because there's no work and nothing to forget once it's set up - you just put your views in with the other views.)

There's a findViewWithTag function, but it only returns one View, which is no good when you have a group you want to grab. So we can write a helper function to grab all descendants of a ViewGroup (not just its immediate children):

fun ViewGroup.getAllDescendants(): Sequence<View> =
        children   children.filterIsInstance<ViewGroup>().flatMap { it.getAllDescendants() }

(This is useful because it can handle Views in a nested hierarchy, so it can help with the filterByInstance approach above too, if you have a container that everything you need is inside, but as part of an internal hierarchy.)

And now you can use that to get every View in the hierarchy, and filter on the tag you want

// start with the root view, e.g. 'view' or 'binding.root' - or any container
// you want to search inside
(view as ViewGroup).getAllDescendants()
        // you don't necessarily need to do this one, but it's safer and casts for you
        .filterIsInstance<ImageView>()
        .filter { it.tag == "cool image" }

CodePudding user response:

If the ImageViews are on the same parent layout you can just query the layout for its children.

You get a childrenlist.

Iterate the list.

You do not at forehand have to know how much image views there are.

  • Related