4

I'm flattening my layouts by employing the new ConstraintLayout (1.1.2) but I can no longer control the visibility of a ProgressBar when it's in a group. Here's the simplest version of my layout XML file which reproduces the issue:

    <android.support.constraint.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <!-- This group prevents me from hiding ProgressBar -->
        <android.support.constraint.Group
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:constraint_referenced_ids="imageLoadingSpinner" />

        <ProgressBar
            android:id="@+id/imageLoadingSpinner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </android.support.constraint.ConstraintLayout>

I've introduced the Group in order to control some of the layout, although for simplicity I've left all that out. The essence of the bug is introducing the group prevents me from setting the ProgressBar's visibility to GONE.

The following code no longer hides the ProgressBar:

    find(R.id.imageLoadingSpinner).setVisibility(GONE);

To be clear, if I remove the group, as shown below, I can set the ProgressBar's visibility to GONE:

    <android.support.constraint.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <!-- Commenting out this group allows me to hide ProgressBar -->
        <!--<android.support.constraint.Group-->
            <!--android:layout_width="wrap_content"-->
            <!--android:layout_height="wrap_content"-->
            <!--app:constraint_referenced_ids="imageLoadingSpinner" />-->

        <ProgressBar
            android:id="@+id/imageLoadingSpinner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </android.support.constraint.ConstraintLayout>

I can't find any information about this issue. ProgressBar visibility issue is unrelated.

Michael Osofsky
  • 11,429
  • 16
  • 68
  • 113

3 Answers3

5

While, as has been said, you can't change the visibility of just a single Group's child (and for that I find this widget much less helpful and fun), you can always set its alpha property.

So if you don't mess in any way with an alpha value of a view you want to hide, you can just do the following:

find(R.id.imageLoadingSpinner).setAlpha(0);

And I can't quite find a reason not to do this, except maybe the fact that the view will probably still be rendered and then all the rendered pixels will be converted to 0-alpha and thus will become invisible wasting a few clock's cycles, but in most cases it would be an exaggeration to consider this as a problem.

Android developer
  • 1,272
  • 1
  • 12
  • 17
  • 1
    Clever idea, thank you. I agree with you that the Group widget is much less helpful because you can't control the visibility of its children independent of the Group. I prefer a solution that controls visibility using setVisibility because it's easier to maintain (in particular, future developers on the project can understand it more quickly than tricks with setAlpha). Thank you though! – Michael Osofsky Jul 26 '18 at 20:54
  • 1
    "... and for that I find this widget much less helpful and fun" - you can say that again! This somewhat counter intuitive behavior just cost me 2 hours... – david.mihola Nov 22 '18 at 16:58
2

I don't think it's an issue because essentially that's the use of the Group, according to the docs:

This class controls the visibility of a set of referenced widgets.

Also:

The visibility of the group will be applied to the referenced widgets. It's a convenient way to easily hide/show a set of widgets without having to maintain this set programmatically.

So you have to set the visibility on the group itself. I don't know what you use the group for, because you didn't specify, but maybe you should restructure to better take advantage of it, or get rid of it completely.

Suleyman
  • 2,765
  • 2
  • 18
  • 31
  • I see, I didn't think the visibility of the group would take precedence over the children because the layout I'm converting had a LinearLayout instead of a group and the visibility logic worked. So I think I'll just use a barrier instead of a group. – Michael Osofsky Jun 28 '18 at 00:14
  • 1
    @MichaelOsofsky Yep, a barrier constraint to the side of a view can work as well. – Suleyman Jun 28 '18 at 06:21
  • 3
    This behaviour seems wrong and stupid. Thanks for the correct answer. – Reasurria Nov 29 '18 at 08:00
1

Just to add to the variety of solutions - you could extend Group and make it behave like you would expect. It's a bit more convenient if you use groups a lot and you expect normal parent-child behaviour in terms of visibility.

public class LayoutGroup extends Group {
    public LayoutGroup(Context context) {
        super(context);
    }

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

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

    @Override
    public void updatePreLayout(ConstraintLayout container) {
        int visibility = this.getVisibility();
        float elevation = 0.0F;
        if (Build.VERSION.SDK_INT >= 21) {
            elevation = this.getElevation();
        }

        for(int i = 0; i < this.mCount; ++i) {
            int id = this.mIds[i];
            View view = container.getViewById(id);
            if (view != null) {
                // If the group is visible, let the child control visibility
                view.setVisibility(visibility == VISIBLE ? view.getVisibility() : visibility);
                if (elevation > 0.0F && Build.VERSION.SDK_INT >= 21) {
                    view.setElevation(elevation);
                }
            }
        }
    }
}

For reference, here is the code from the base class (Group):

  for(int i = 0; i < this.mCount; ++i) {
        int id = this.mIds[i];
        View view = container.getViewById(id);
        if (view != null) {
            view.setVisibility(visibility);
            if (elevation > 0.0F && VERSION.SDK_INT >= 21) {
                view.setElevation(elevation);
            }
        }
    }

Then in your layout file of course you use your own component:

   <yourpackage.LayoutGroup
            android:id="@+id/group_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:constraint_referenced_ids="view1,view2,view3"/>
Reasurria
  • 1,808
  • 16
  • 14