Search code examples
android-constraintlayout

MATCH_CONSTRAINTS not working in dynamically generated weighted chain


I have written the following method. It dynamically creates 16 red view boxes, and then adds them to a constraint layout chain in order to distribute them horizontally across the parent layout. It works, but only if I give the views a specific width value, and I want them to fill the available space. If I set each view's width to ConstraintSet.MATCH_CONSTRAINT, none of them appear at all.

What am I doing wrong? I thought if the width was set to MATCH_CONSTRAINT, each view would fill up the space allotted to it by its weight value. Instead, they aren't appearing at all, and they only show up if I give them a specific width but then they won't grow or shrink to fit the view. It's as if setting them to MATCH_CONSTRAINT is the same as setting the width literally to zero.

private fun createDropTargets(parentView: ConstraintLayout) {
        parentView.setOnDragListener(StaffDragListener())
        val set = ConstraintSet()

        val ids = arrayListOf<Int>()
        val weights = arrayListOf<Float>()
        for (i in 0..15) {
            val drop = View(activity)
            drop.setBackgroundColor(Color.RED)

            // THE NEXT LINE IS WHERE I SET THE WIDTH VALUE. IF I SET TO A NUMBER > 0 THE VIEWS SHOW UP, BUT DO NOT DYNAMICALLY FILL THE SPACE
            
            val params = ConstraintLayout.LayoutParams(ConstraintSet.MATCH_CONSTRAINT, 0)
            params.setMargins(5, 0, 5, 0)
            drop.layoutParams = params
            drop.id = View.generateViewId()
            parentView.addView(drop)
            ids.add(drop.id)
            weights.add(1f)
        }
        set.clone(parentView)
        var previousId: Int? = null
        for ((index, id) in ids.withIndex()) {
            if (previousId == null) {
                set.connect(id, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT)
            } else {
                set.connect(id, ConstraintSet.LEFT, previousId, ConstraintSet.RIGHT)
                if (index == ids.size - 1) {
                    set.connect(id, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT)
                }
            }
            previousId = id
        }
        set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, ids.toIntArray(), weights.toFloatArray(), ConstraintSet.CHAIN_SPREAD_INSIDE)
        set.applyTo(parentView)
    }

Solution

  • MATCH_CONSTRAINT is setting the width/height to zero because it is defined as follows in ConstraintSet:

    public static final int MATCH_CONSTRAINT = 0;
    

    What this says is that the constraints will define the size. No contraints, or improper constraints, will just result in a zero width/height.

    Your code is correct except that the height is set to MATCH_CONSTRAINT (remember 0 == MATCH_CONSTRAINT) but you do not apply constraints to the height so the views just disappear (width but no height.) If you supply a set height to the views, they will appear. The following code will show the views with a height of 100px:

    private fun createDropTargets(parentView: ConstraintLayout) {
        parentView.setOnDragListener(StaffDragListener())
        val set = ConstraintSet()
    
        val ids = arrayListOf<Int>()
        val weights = arrayListOf<Float>()
        for (i in 0..15) {
            val drop = View(activity)
            drop.setBackgroundColor(Color.RED)
            val params = ConstraintLayout.LayoutParams(ConstraintSet.MATCH_CONSTRAINT, 100)
            params.setMargins(5, 0, 5, 0)
            drop.layoutParams = params
            drop.id = View.generateViewId()
            parentView.addView(drop)
            ids.add(drop.id)
            weights.add(1f)
        }
        set.clone(parentView)
        set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, ids.toIntArray(), weights.toFloatArray(), ConstraintSet.CHAIN_SPREAD_INSIDE)
        set.applyTo(parentView)
    }
    

    By the way, the second loop is redundant. All that work is accomplished with the creation of the chain.

    You will have to decide if you want a defined height or if you will let the height be set by the appropriate constraints applied in the vertical direction.