34

I have a ConstraintLayout.Group defined like this:

    <android.support.constraint.Group
        android:id="@+id/someGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="
        textView1,
        textView2,
        button1" />

I change the visibility of this group from GONE to VISIBLE:

someGroup.visibility = VISIBLE

But when I try to override it by specifying the visibility of one of the views in that group:

button1.visibility = GONE

...it doesn't work. I log this visibility to logcat and it says 8 (GONE), but I can still see the view.

Any ideas what might be happening here? I tried calling requestLayout and updatePreLayout on this group, I tried changing the visibility a few times, visible, invisible and then gone. I even rebuilt the whole project because some stackoverflow answer said that it might help for visiblity issues in ConstraintLayout. I also tried versions 1.1.3 and 2.2.0-alpha. Nothing worked. It's always visible.

Michał Klimczak
  • 12,674
  • 8
  • 66
  • 99

1 Answers1

38

Update: The behavior of individual view visibility within a group has been change and is reported as fixed in ConstraintLayout version 2.0.0 beta 6. See bug fixes for ConstraintLayout 2.0.0 beta 6 .


You are seeing the correct behavior. When you place a view within a group, you give up the ability to change the individual view's visibility. I think the mechanics are that the view's visibility is set (by you) then the group's visibility is assigned (by the system) based upon group membership.

The workaround is dependent upon your needs:

  1. Keep the view you want to control the visibility of out of a group;
  2. Manage your own group logic and forget the groups offered by the system;
  3. Manage group membership using setReferencedIds.

I think that this is a common complaint, but I also think that it is unlikely to be addressed. (IMHO)


I just commented on another question regarding this exact issue, so I have taken the liberty to post here (although the answer is already accepted) a simple class that will help manage a ConstraintLayout group.

ManagedGroup.java

/**
 * Manage a ConstraintLayout Group view membership as a view's visibility is changed. Calling
 * {@link #setVisibility(View, int)} will set a view's visibility and remove it from the group.
 * Other methods here provide explicit means to manage a group's view membership.
 * <p>
 * Usage: In XML define
 * <pre>{@code
 * <[Package].ManagedGroup
 *         android:id="@+id/group"
 *         android:layout_width="wrap_content"
 *         android:layout_height="wrap_content"
 *         android:visibility="visible"
 *         app:constraint_referenced_ids="id1,id2,id3..." />}
 * </pre>
 */
public class ManagedGroup extends Group {
    private final Set<Integer> mRemovedRefIds = new HashSet<>();

    public ManagedGroup(Context context) {
        super(context);
    }

    public ManagedGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ManagedGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * Set the reference ids for the group and clear the removed id array.
     *
     * @param ids All identifiers in the group.
     */
    @Override
    public void setReferencedIds(@NonNull int[] ids) {
        super.setReferencedIds(ids);
        mRemovedRefIds.clear();
    }

    /**
     * Set visibility for  view and remove the view's id from the group.
     *
     * @param view       View for visibility change
     * @param visibility View.VISIBLE, View.INVISIBLE or View.GONE.
     */
    public void setVisibility(@NonNull View view, int visibility) {
        removeReferencedIds(view.getId());
        view.setVisibility(visibility);
    }

    /**
     * Add all removed views back into the group.
     */
    public void resetGroup() {
        setReferencedIds(getAllReferencedIds());
    }

    /**
     * Remove reference ids from the group. This is done automatically when
     * setVisibility(View view, int visibility) is called.
     *
     * @param idsToRemove All the ids to remove from the group.
     */
    public void removeReferencedIds(int... idsToRemove) {
        for (int id : idsToRemove) {
            mRemovedRefIds.add(id);
        }

        int[] refIds = getReferencedIds();
        Set<Integer> newRefIdSet = new HashSet<>();

        for (int id : refIds) {
            if (!mRemovedRefIds.contains(id)) {
                newRefIdSet.add(id);
            }
        }
        super.setReferencedIds(copySetToIntArray(newRefIdSet));
    }

    /**
     * Add reference ids to the group.
     *
     * @param idsToAdd Identifiers to add to the group.
     */
    public void addReferencedIds(int... idsToAdd) {
        for (int id : idsToAdd) {
            mRemovedRefIds.remove(id);
        }
        super.setReferencedIds(joinArrays(getReferencedIds(), idsToAdd));
    }

    /**
     * Return int[] of all ids in the group plus those removed.
     *
     * @return All current ids in group plus those removed.
     */
    @NonNull
    public int[] getAllReferencedIds() {
        return joinArrays(getReferencedIds(), copySetToIntArray(mRemovedRefIds));
    }

    @NonNull
    private int[] copySetToIntArray(Set<Integer> fromSet) {
        int[] toArray = new int[fromSet.size()];
        int i = 0;

        for (int id : fromSet) {
            toArray[i++] = id;
        }

        return toArray;
    }

    @NonNull
    private int[] joinArrays(@NonNull int[] array1, @NonNull int[] array2) {
        int[] joinedArray = new int[array1.length + array2.length];

        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }
}
Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • I was afraid this might be the answer. Let me wait a few days and I'll mark as correct if no one replies anything different. – Michał Klimczak Nov 28 '18 at 06:04
  • 13
    That's correct. And is a pretty dumb API. Just because one assigns a group to a view doesn't mean they want to control visibility uniquely through it. I refuse to believe that Google actually designed it to be that way and thought it was a good idea. – Gabriel Vasconcelos Feb 04 '19 at 13:39
  • 3
    ConstraintLayout 2.0.0 beta 6 allows setting visibility for individual views but works only if you set visibility programmatically – Leo DroidCoder Oct 21 '20 at 20:03
  • Very unfortunate that this is working as designed. No ability to change individual view visibility. https://issuetracker.google.com/issues/73186245 – Markymark Mar 02 '23 at 00:24