Effectively Wrangling Together a Bunch of Views

09 Nov 2018

One bit of task that I find myself doing over and over again is managing a bunch of Views and their visibility. In the olden days <insert old person handwave>, before there was ConstraintLayout, I have written my fair share of container_s to make this task manageable. Say we have to do something like this:


Now you see me, now you don't!

If you asked me two years ago to implement the layout above, I would have:

Or in code:

@BindView(R.id.checkbox) CheckBox mCheckbox;                                                   
@BindView(R.id.container) ViewGroup mContainer;                                                
                                                                                               
@Override                                                                                      
public void onCreate(@Nullable Bundle savedInstanceState) {                                    
    super.onCreate(savedInstanceState);                                                        
    setContentView(R.layout.activity_constraint);                                              
    ButterKnife.bind(this);                                                                    
    mCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {        
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
            if (isChecked) {                                                                   
                mContainer.setVisibility(View.VISIBLE);                                        
            } else {                                                                           
                mContainer.setVisibility(View.GONE);                                           
            }                                                                                  
        }                                                                                      
    });                                                                                        
}

(Here is the Activity code in full, as well as the layout file).

It works, but it’s a whole lot of things to write!

Let’s try to harness the power of the tools we currently have at our disposal. First off, here’s the skeletal ConstraintLayout-ified version of our existing layout file (click here for the full version):

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
    </data>
    <android.support.constraint.ConstraintLayout>

        <ImageView android:id="@+id/image_1" />

        <ImageView android:id="@+id/image_2" />

        <TextView android:id="@+id/description" />

        <CheckBox android:id="@+id/checkbox" />

        <View android:id="@+id/divider" />

        <TextView android:id="@+id/section_description" />

        <TextView android:id="@+id/section_title" />

        <Button android:id="@+id/button_2"/>
    </android.support.constraint.ConstraintLayout>
</layout>

Immediately we see that the view hierarchy is so much flatter, and there are no unnecessary nestings at all! But then, the next question arises: doesn’t this mean that when we have to deal with managing the visibility of the required widgets we could either end up writing more code or introduce some level of nesting?

Well, not quite! We can use a ConstraintLayout feature called Group that would make managing our desired behaviour much much easier. A Group allows us to reference a comma-separated list of widget IDs and directly manage their visibility – which is exactly what we want to achieve!

Let’s go ahead and make the Group:

<android.support.constraint.Group
    android:id="@+id/group"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="divider,section_description,section_title"/>

And now all that’s left is actually hiding or showing it. For this demo, I opted to use the same technique as in my previous post and use a data binding expression:

android:visibility="@{checkbox.checked ? View.VISIBLE : View.GONE}"

We can now simplify our Activity code to become:

public override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    DataBindingUtil.setContentView<ActivityConstraintBinding>(this, R.layout.activity_constraint)
}

Fun!