Home > other >  Why does my setOnClickListener only apply to last element each loop pass?
Why does my setOnClickListener only apply to last element each loop pass?

Time:05-11

Sorry if the code is a bit convoluted, I've been spamming things out trying to fix this for ages.

I create children within a layout and track the number in which they were added, then compare them so we know which sets belong to which exercises. On click of the arrow, the set information should collapse but only the last view of each exercise does.

fully loaded

clicking to hide

all buttons clicked to hide

    fun listeners() {
for (i in childCount3) {
    val imageButton: TextView = binding.parentLinearLayout.getChildAt(i).weightTxt
    val fragment = EditSetFragment()
    imageButton.setOnClickListener {
        supportFragmentManager.beginTransaction().replace(R.id.parent_linear_layout, fragment).commit()
    }
}
// childList = 10 | childList2 = [0,3,6,8] | childList3 = [1,2,4,5,7,9]
var testCount = 0
var testCount2 = 0
for (i in 0 until childCount2.size) {
    var matches = ArrayList<Int>()
    val imageButton: ImageButton = binding.parentLinearLayout.getChildAt(childCount2[i]).expandCard
    Log.d("I  ", i.toString())
    for (k in 0 until childCount3.size) {
        Log.d("k  ", k.toString())
        Log.d("CHECKER  ", binding.parentLinearLayout.getChildAt(childCount2[i]).instanceTxtEx.text.toString()   binding.parentLinearLayout.getChildAt(childCount3[k]).instanceTxt.text).toString()
        if(binding.parentLinearLayout.getChildAt(childCount2[i]).instanceTxtEx.text == binding.parentLinearLayout.getChildAt(childCount3[k]).instanceTxt.text) {
            matches.add(childCount3[k])
            Log.d("MATCH  ", i.toString())
            imageButton.setOnClickListener{
                binding.parentLinearLayout.getChildAt(childCount3[k]).visibility = GONE
            }

        }   
    }
}
}

CodePudding user response:

It's a little hard to follow what you're doing, but I think you're going over each item in childCount2, grabbing its expand button, then trying to add a click listener for each child that item has in childCount3?

The problem is you can only set one click listener on a View, so in your loop you're not adding multiple listeners which each hide a button, you're replacing the current listener each time. So it ends up only hiding the last item you set a listener for.

If you're adding these items to the layout programmatically, can't you just store a list of the children of each top-level item? That way you can just set the listener once for each button, and in it just reference your list of its children. Something like this:

val parentsAndChildren = mutableMapOf<View, MutableList<View>>()

// Add your top-level (childCount2) views here, mapped to an empty list
// As you add children (childCount3), grab their parent's list and add the child to it

fun setUpCollapseButtons() {
    // iterate over all the parents and set a click listener on their buttons
    parentsAndChildren.keys.forEach { parent ->
        parent.expandCard.setOnClickListener {
            // grab the current list of children for this parent, and do the thing
            parentsAndChildren[parent].forEach { it.visibility = GONE }
            // could also do { it.visibility = if (it.visibility == VISIBLE) GONE else VISIBLE } for a toggle
        }
    }
}

I'm not 100% sure how you're referencing things like expandCard when getChildAt just returns a View, but you can use findViewById on each parent when you're setting up the click listeners. Hopefully that gives you the idea - it's much easier to just keep track of the views themselves, instead of having to go digging for them (and doing text comparisons to match them)


If you still want to do it this way, here's how you can set one click listener on each:

// collect all the parent Views
val parents = childCount2.map { i -> binding.parentLinearLayout.getChildAt(i) }
parents.forEach { parent ->
    // collect all its children
    val children = childCount3.map { j -> binding.parentLinearLayout.getChildAt(j) }
        .filter { child -> parent.instanceTxtEx.text == child.instanceTxtEx.text  }

    // add a single click listener to the parent, make it affect all the children
    parent.expandCard.setOnClickListener {
        children.forEach { it.visibility = GONE }
    }
}

There are fancier ways to collect the parents and children, but hopefully that makes sense - you need to collect them all before you can set the click listener that affects them all

  • Related