Unwrapping Framework Binding Adapters

15 Feb 2019 | tags: android data binding

For the past year or so, my team has been all-in with data binding. And if you know me at all, it obviously makes me one happy duck!

Over time the team has moved from seeing data binding as a Butterknife replacement to utilising more data binding features. In fact, we have built an arsenal of @BindingAdapters already!

This is precisely what prompted me to look at what we have currently. And I noticed a curious thing. We have written some Binding Adapters that we did not really need.

For example, we have this one for listening to the IME Action button:

@BindingAdapter("onEditorActionClicked")
fun onEditorActionClicked(editText: EditText, editorActionListener: EditTextEditorActionListener) {
    editText.setOnEditorActionListener { _, actionId, _ ->
        editorActionListener.onEditorActionClicked(actionId)
        false
    }
}

Thing is, we did not really need to write this custom BindingAdapter because one such thing exists in the framework.

It is understandable though that we missed this, because documentation on these things is a bit hard to come by. Even if we look at the framework source code, someone who is not too familiar with data binding might find it a bit hard to parse.

So let me try to help a little bit.

Whenever we want to implement a BindingAdapter – especially if it is going to hook into an existing listener – the first step should be to look at the Binding Adapters implemented in the framework. There’s a whole bunch written for all sorts of widgets (that is, except for AndroidX SearchView, but that’s another story).

Since we want to work with the IME Action for an EditText, we should look at TextViewBindingAdapter.

At the top of the file, we see this annotation: @BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener")

The documentation kind of glosses over what this means; but in a nutshell:

(Note: If you are interested in how the data binding library automagically sets the listeners and manages the interfaces, I highly suggest to checkout the generated binding file of your layout.)

Oooh notice how setOnEditorActionListener is the method we actually call in our own custom BindingAdapter! Looks like we are on to something!

Let’s try to use this attribute then:

<EditText
    android:id="@+id/sample_edit_action"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="text"
    android:imeOptions="actionSend"
    android:onEditorAction="@{() -> handlers.onEditorActionClicked()}"/>

(Note: If you are not familiar with the lambda syntax I am using here, this is called a “listener binding” and you can read all about it here.)

(Second note: Lint will tell you there is no such attribute. Lint lies!)

The next bit we have to do would be to tell our handlers what needs to happen when onEditorAction is called. For simplicity, let’s say we want to show a Toast when the user clicks on the Send button.

fun onEditorActionClicked() : Boolean {
    Toast.makeText(this, "Send was clicked!", Toast.LENGTH_LONG).show()
    return false
}

The implementation is fairly straightforward, but one thing is important though: make sure the implementation has the same exact return value type as the interface (otherwise data binding gets into a StackOverflowException)!

Now what if we actually need the KeyEvent or the ActionId? Let’s update our implementation to factor those in:

fun onEditorActionClicked(view: TextView, actionId: Int?, event: KeyEvent?) : Boolean {
    when(actionId) {
        EditorInfo.IME_ACTION_SEND -> {
            Toast.makeText(this, "Send was clicked!", Toast.LENGTH_LONG)
                    .show()
        }
    }
    return false
}

And let’s update our layout file as well:

<EditText
    android:id="@+id/sample_edit_action"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="text"
    android:imeOptions="actionSend"
    android:onEditorAction="@{(v, id, event) -> handlers.onEditorActionClicked(v, id, event)}"/>

And we can finally delete our custom BindingAdapter! :tada:

:exclamation: Remember as well that data binding by default looks for methods prefixed with set. This means that if we can call a setter programmatically, we do not have to make a BindingAdapter for it. Some examples are setEnabled(), setBackgroundColor(), etc.

So in summary:

Have fun binding!


Okay Google, Show Me a Million Errors

07 Dec 2018 | tags: android

EDIT
Aaaaaaaand an update literally two minutes after I posted this: I was told that the data binding fix made it to the latest Android Studio Canary release.

I still opt to keep the config in my gradle file, but should be less necessary now.
/EDIT

Our team has been super busy lately.

Aside from working on two big feature changes, we have been working on merging our AndroidX migration. It is a lot changes!

If you have used data binding before, chances are you have come across this one pervasive issue: when any annotation processor fails, data binding throws up and you end up seeing a million errors in your logs.

We use data binding quite a bit in the app, so when a build fails (and there’s a good chance they will!) during this transition period, we see so many errors. So. Many.

There are plans to fix this though!

Anyway, we have so many errors that all I see are data binding issues and the real actual issue is invisible. I have no idea where to look first.

BUT! TIL that we have a way of actually seeing more errors!

If you’re using Kotlin, Kapt provides some Java compiler options:

kapt {
    javacOptions {
        // Increase the max count of errors from annotation processors.
        // Default is 100.
        option("-Xmaxerrs", 500)
    }
}

And for the Java variant:

gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
        options.compilerArgs << "-Xmaxerrs" << "500"
    }
}

Thank you so much to @Yigit Boyar for sharing this tip with me!


Ya Basic

02 Dec 2018 | tags: android

Over the last year or so, we have been writing a lot of Kotlin at work. There is a consensus within the team that we all like working with the language. It really helps our productivity a lot by reducing a lot of boilerplate, we can actively enforce nullability rules that makes business logic obvious, and having the option to make extension functions offers a lot of flexibility.

However, as a wise man once said, with great power comes great resposibility. There have been a few times where I see code written in a “Kotliny” way that I feel makes things look more complicated than it should be.

Take this one I saw at a recent pull request:

isSetUpDone?.run {
   if (shouldShowNotification()) {
       showNotification()
   }
} ?: showNotification()

I stared at that piece of code for five minutes, trying to figure out what it does. I read and re-read. Maybe I am too stupid to understand it. I asked the author – “Maybe put a comment in to figure out what this is doing? It is a bit hard to read.” I got a response: “Let me come over and explain.” He then proceeded to explain to me what run and ?: does (and I did not have the energy to ask him to stop mansplaining 🙄 ).

Now, my friends, that is not the point; and I have talked a bit about this in the past.


Complex != Genius

Just because something looks complex does not mean it’s ingeniuty at work. What if somebody else new joins the team? What if somebody else needs to make changes to this feature? What if the original author left the company and won’t be there to come over and explain anymore?

After talking to the author, we have come to the conclusion that what we wanted to do was call showNotification() only if all conditions are satisfied. So really, we can move isSetUpDone (which *is* one of the conditions!) to inside the shouldShowNotification() function, and we end up with a much simpler, much more readable implementation.

if (shouldShowNotification()) {
   showNotification()
}

If there is one thing I have learned from years and years of writing and reading code, it is that it takes a lot more time and effort to make things as simple as possible.

(via GIPHY)

We need to be careful that we do not fall into the trap of “How do I write this in Kotlin?”; that instead of leveraging the language, we end up abusing it.

I guess sometimes we get too caught up with making sure we can show off our chops and use these fancy new language features that we lose sight of what is really important – doing our bit in writing code that lasts forever.