Effectively Wrangling Together a Bunch of Views

09 Nov 2018 | Comments | tags: android data binding constraint layout

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!


Referencing IDs in Data Binding

13 Oct 2018 | Comments | tags: android data binding

Last week, I was talking to someone on my team and it became apparent that they weren’t aware of one super useful feature of data binding. If you know me at all, you know that I like love this library, and I would take every opportunity to spread the love around.

Let’s take a very simple example – say we have a CheckBox and a TextView in a layout file, and we want the TextView to display if the CheckBox is checked or not. I made this adorable gif to illustrate:


Cheeky!

The corresponding layout file is pretty straightforward:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>

    </data>
    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <CheckBox
            android:id="@+id/cheeky_checkbox"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/cheeky_checkbox"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>
        <TextView
            android:id="@+id/textview"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingTop="8dp"
            android:paddingStart="8dp"
            app:layout_constraintTop_toBottomOf="@id/cheeky_checkbox"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>
    </android.support.constraint.ConstraintLayout>
</layout>

Let us first define our string resource:

<string name="is_it_checked">Is it checked? %1$s</string>

where %1$s would be the checked state of the CheckBox.

All that is left is to use this string in our layout file! We can use two data binding features: String formatting, and ID referencing.

android:text="@{@string/is_it_checked(cheekyCheckbox.checked)}"

Yes, my friend – we can reference other views in our data binding expressions via their ID! Note that data binding converts IdsToCamelCase, so be sure to follow the convention.

Happy binding!


Time Check: 10 Minutes

12 Sep 2018 | Comments | tags: speaking conference

Last year, I wrote about my talk preparation process. I mentioned that I try to practice my talk verbatim a bunch of times, especially the night prior.

I have been trialling out a new strategy for my talks recently and I think it works really well.

During one of my practice runs, I keep a notepad beside me and jot down a few key slides in my talk. The more evenly-spaced out they are, the better. I then set a stopwatch (I use Timely), and start practicing.

Once I hit those slides I have written down, I put that down as a lap and jot the time down across the slide number. I keep on going until I finish the whole talk.


Keeping time (and please pardon my handwriting)

I try to do this at least twice – the more data points the better! I found out that my lap times tend to diverge +/- a minute or so on each iteration. Once I am happy with the general timing, it’s time to make the times official!

I then add the times to my speaker notes, making sure I use a BIG FONT so that it’s easy to read.


Time for a time check!

I find that having those time checks in there help make sure that I pace myself well during the actual talk. I tend to speak fast when I am nervous, so knowing that I am going at the right speed helps calm me down a bit.

I cannot rely on just the number of slides remaining, as some take longer or shorter than others (I had a slide during my talk at Mobile Refresh Wellington that took me two minutes to get through 😝). Having the time written down means I can concentrate on what I have to say without worrying about timekeeping in my head!

Bonus tip: Taking a sip of water helps calm down the nerves! Highly recommend to add “WATER BREAK! 🥛” notes as well.