Bill Phillips - Big Nerd Ranch Tue, 19 Oct 2021 17:46:11 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 Writing Android Programming, The 3rd Edition https://bignerdranch.com/blog/writing-android-programming-the-3rd-edition/ https://bignerdranch.com/blog/writing-android-programming-the-3rd-edition/#respond Mon, 26 Dec 2016 10:53:37 +0000 https://nerdranchighq.wpengine.com/blog/writing-android-programming-the-3rd-edition/ Whenever we start a new edition of the Android book, our first question is what you would expect—what new things do we want to put in the book? And for many books, that would be the start and end of it. However, at Big Nerd Ranch, things can be a bit more complicated.

The post Writing Android Programming, The 3rd Edition appeared first on Big Nerd Ranch.

]]>

Whenever we start a new edition of the Android book, our first question is what you would expect: what new things do we want to put in the book? And for many books, that would be the start and end of it: you decide on the topic, you write the coverage of the topic. And then you’re done.

Things can be more complicated than that for us, though. I’ll use ConstraintLayout as a case to show why and how this is the case.

The Dream

Before we started working with ConstraintLayout, Chris and I were absolutely sold on it.
We had read blog posts about it, watched talks and believed in what it was trying to do.

ConstraintLayout is a replacement for RelativeLayout.
Now, we have always taught RelativeLayout.
It could do things no other layout can do.
We have never been big fans of it, though: It often performs poorly, and even if it performs well it’s still unreadable.

ConstraintLayout is supposed to fix both of these problems.
Its performance is comparable to RelativeLayout, but its constraint system is capable of so much more that you should be able to reduce nesting, which is a big performance benefit.

More importantly, though, ConstraintLayout was introduced to make real visual layout editing possible.
Visually editing nested LinearLayouts and RelativeLayouts wasn’t really a visual process, so much as it was a way of visualizing the XML you were tweaking and rearranging under the hood.
With ConstraintLayout, there is a visual metaphor of tying anchors to edges.
It corresponds directly to what’s under the hood in a way that was never the case for the old layout tools.

Some developers prefer the security and sense of control that direct XML editing provides, but I’m a believer in visual editors. Once you’ve gotten used to one and seen how much better it is to directly manipulate the resulting view, you can never be satisfied by mixed-mode editing again. I love Android, but iOS has always had a leg up on Android in visual layout editing.

Starting the Work

So here we are: day one of working on ConstraintLayout. “Maybe we should update THE ENTIRE BOOK to use ConstraintLayout.” Such is the mood when my coauthor Chris Stewart gets to working.

Five Minutes Later…

Five minutes later, the mood has utterly changed. Chris is shaking his head and sighing. “ConstraintLayout is horrible. Nobody should use it.”

This kind of thing happens. And it matters to us. When we write, we’re just like you or anyone else: we use a tool, we see how useful it is. If we love it, we ramp up our usage of it. If we don’t, we back off a bit. Data binding? Ramp it up. Loaders? Eh, let’s back it off.

In his initial take at working with ConstraintLayout, Chris had found that his experience just didn’t match up with his expectations. The layout he made visually ended up looking totally different when he ran it, and he had a beast of a time getting the visual editor to cooperate in the first place.

What to Do?

Our job is to dig deep into these tools and figure out the good way, the right way to use them, if there is one. That means digging into things longer and deeper than we normally would.

It’s not uncommon for us to disagree on this. That’s what happened here: Chris, having used the deal, hated it. I, having not used it at all, loved it. (Obviously, I was right.)

The push and pull in this case ended up in multiple passes on the ConstraintLayout chapter by both of us. We found that some of the sharper edges of the tool could be sanded down — they needed some more emphasis than they were given in the docs. And some of those edges were the result of the tool just having a different model for thinking about ideas we’d grown used to over time, like layout width and height.

The Final Result

Any good creative work is a sort of crash landing that happens between what you envision and what you can actually make. Did we put ConstraintLayout everywhere in the book like I wanted to? Well… no, we didn’t. Is it relegated to the back of the book? Nope, that’s not the case, either.

And I think that makes sense. Our book is a reflection of what we practice. Much as we loved playing with ConstraintLayout, we do not rely on it habitually yet. Until we do, until we believe in it enough to use it habitually in our practice, our book will not habitually use ConstraintLayout, either. And that’s the way it should be.

So that’s our third edition: a snapshot of where we are today. Hopefully, we’ll be lucky enough to have a fourth edition. Until then, though, you can expect that we’ll be pushing our own practice forward as much as we can.

The post Writing Android Programming, The 3rd Edition appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/writing-android-programming-the-3rd-edition/feed/ 0
Shades of MVVM https://bignerdranch.com/blog/shades-of-mvvm/ https://bignerdranch.com/blog/shades-of-mvvm/#respond Thu, 20 Oct 2016 10:00:53 +0000 https://nerdranchighq.wpengine.com/blog/shades-of-mvvm/ Depending on who you're talking to, MVVM can mean different things. In this post, I'm going to present a few approaches to MVVM used by people I've personally talked to—folks in the data binding community, in our team here at Big Nerd Ranch, and from a great talk I heard at Droidcon NYC.

The post Shades of MVVM appeared first on Big Nerd Ranch.

]]>

This September I went to Droidcon NYC in Manhattan, down in the Financial District.
One talk that got my mind going was given by Florina Muntenescu.

It was called A Journey Through MV Wonderland.
The talk is a lucid explanation of the relationships between MVC, MVP and MVVM. Check it out:

A quick aside before we get started:

What set my particular brain off, though, was the discussion of MVVM.
The whole talk is enlightening, but I want to call particular attention to this diagram:

MVVM as described in A Journey Through MV Wonderland

(Original slide here)

I’ve been interested in what people are actually doing when they’re writing MVVM-architected apps.
So I found it nice to see one clear interpretation of MVC, MVP and MVVM in this talk.

That’s not always the case, though.
Depending on who you’re talking to, MVVM can mean different things.
In this post, I’m going to present a few approaches to MVVM used by people I’ve personally talked to—folks in the data binding community, in our team here at Big Nerd Ranch, and from Florina’s talk.

My point won’t be to figure out which of these is correct or right, but just to show what I’m familiar with. Correctness is more of an admirable goal in these arch discussions than a useful yardstick. So if you read one of these diagrams and think, “Oh no, I’m/we’re doing it wrong”— chillax. If your code runs, the people using your app are happy, turnover is low and nobody’s lying to themselves about anything important, then you’re doing it right. Those are the only yardsticks that truly matter.

Classic Binding ViewModel

Binding MVVM

When we first started implementing MVVM in our projects, we implemented something like the above, which I’m calling “classic binding MVVM.” (I’ll explain why in a moment).

In this model, the ViewModel exposes methods that do one of two jobs:

  1. Reveal display data, or
  2. Invoke actions from the user interface.

We first tried this out in an experimental Kotlin project called KöffeeKüp, an app that helped us keep track of how frequently we were brewing coffee.
One ViewModel was called CarafePlacardViewModel, and had methods that looked like this:

    @Bindable
    fun getCarafeDrawable(): Drawable {
        var timeSinceLastWashMs = clock.currentTimeMillis() - lastWashTimeMs

        var resId = CarafeMentalHealth.getDrawableResId(timeSinceLastWashMs)
        return context.resources.getDrawable(resId, context.theme)
    }

    fun resetTimer() {
        lastWashTimeMs = clock.currentTimeMillis()
        timeStore.setLastWashTimeMs(carafe, lastWashTimeMs)
        notifyPropertyChanged(BR.carafeDrawableResId)
    }

The first method changes what image is displayed depending on how old the coffee is.
(The older the coffee is, the grodier the carafe image gets.)
And the second method is invoked when the button for the timer is invoked.

In this way of doing things, everything can be wired up from the layout file using the Data Binding framework:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="viewModel"
            type="com.bignerdranch.android.koffeekup.viewmodel.CarafePlacardViewModel"/>
    </data>

    ...

                <ImageView
                    ...
                    android:src="@{viewModel.carafeDrawable}"/>

    ...

            <Button
                ...
                android:onClick="@{() -> viewModel.resetTimer}"/>

    ...
</layout>

The reason I called this “classic binding ViewModel” is that this is roughly the way the architecture was originally pitched when it was originated in Microsoft’s tool stack.
John Gossman pitched it all the way back in 2005, and it has been recommended for apps that use data binding ever since.

Rx MVVM

A closely related version of the same idea is to use the same MVVM architecture, but instead of using Data Binding to glue your ViewModel to your View, you use RxJava Observables.

RxMVVM

The difference between this and the classic binding VM approach is all in the implementation. The Binding VM approach relies on Data Binding in your layout file; here, you write code in a View class, and rely on RxJava to communicate with your ViewModel.

So CarafePlacardViewModel would instead look like this:

    fun getCarafeDrawable(): Observable<Drawable> {
        return Observable.interval(100, TimeUnit.MILLISECONDS)
            .map { time -> clock.currentTimeMillis() - lastWashTimeMs }
            .map { timeSinceLastWashMs ->
                val resId = CarafeMentalHealth.getDrawableResId(timeSinceLastWashMs)
                context.resources.getDrawable(resId, context.theme)
            }
    }

    fun resetTimer() {
        lastWashTimeMs = clock.currentTimeMillis()
        timeStore.setLastWashTimeMs(carafe, lastWashTimeMs)
        notifyPropertyChanged(BR.carafeDrawableResId)
    }

CarafePlacardView would be a proper Kotlin (or Java) class that would wire up to the appropriate Observables:

class CarafePlacardView(..., private val viewModel: CarafePlacardViewModel) {
    ...

    fun setup() {
        viewModel.carafeDrawable.subscribe { drawable ->
            placardImage.imageDrawable = drawable
        }
        resetTimerButton.onClickListener = { view ->
            viewModel.resetTimer()
        }
    }
}

The responsibilities of the two classes are exactly the same.
The only difference is how they’re implemented:

  • Instead of using Data Binding’s Observable interface with @Bindable getters, RxJava Observables are used.
  • Instead of using a layout file with Data Binding wiring within it, a View classes is implemented in code.

I can’t speak to the advantages of this pattern over the Data Binding version, but it appears that it’s a nice option if you don’t want to dive in with Data Binding.

MVVM…C?

In my discussions with other developers, I’ve found that a few folks have independently started using a variation on the classic binding VM pattern.
I have not found any blog posts documenting it, so I’ll talk about it here.

At Big Nerd Ranch, we felt like our ViewModels were starting to get a little too complicated.
So we decided to pull out the user actions into our fragments:

RxMMVVMCVVM

While we have used this approach, I don’t know how much I can recommend it.
This appears to have pushed a lot of code into our fragments, which isn’t under test. I don’t know if that’s a consequence of this pattern or if it’s unrelated, but it does look a little grody in the diagram—the fragment is taking on a lot of jobs here.

Outside of Big Nerd Ranch, people have been taking this one step further by defining an interface for the user actions in their layout file:

MVVMCallback

This is the approach taken by the majority of people I know who are on the leading edge of working with data binding.
In this way of doing things, more care is taken to separate out the callback and its implementation.
It’s isolated completely from the ViewModel, and the layout file is abstracted from the implementation by an interface.
I haven’t tried this myself, but it looks like a solid approach.

So What Do I Do?

Well, if you asked me to choose an architecture pattern up front today to guide my work on a new project, I’d go with the classic binding ViewModel approach.
I’ve worked with it a little bit already, and I found it to be a good friend in improving my app’s testability. I’m really interested in integrations with RxJava, though—I’d love to be able to integrate that into my use of data binding somehow.

Even though that’s what I like, though, I do not recommend rearchitecting your existing app based on my recommendation, just because I said it is the “best”.
Evaluating architectures in that way is misleading and and often frustrating.
You can find opinions like mine easily, but head-to-head comparisons between different architecture patterns with working code are much harder to find, much less compare. The goodness or badness of architecture has everything to do with how your code grows and changes within that architecture, so these snapshot comparisons are almost useless as individual datapoints. That often makes discussions of architecture are extremely annoying.

And yet: I love hearing about application architectures. I see at least two or three arch oriented talks at every conference I attend. What makes them great is when they tell a story about how a group of engineers encountered a series of challenges, and how they adapted their application to meet those changes.

There is no spreadsheet to compare these stories in, or metric to ascertain which is “best”.
Those stories may shed light on your situation, or they may not.
They’ll never be identical to yours, though, because architecture is as specific as your team itself is.

So that’s what I really recommend: don’t take any of this gospel. Ask, “What problems do I actually have with keeping this codebase clean? Can one of these approaches solve it?” And until then, just keep reading.

The post Shades of MVVM appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/shades-of-mvvm/feed/ 0
Gearing up for the Third Edition of Android Programming: The Big Nerd Ranch Guide https://bignerdranch.com/blog/gearing-up-for-the-third-edition-of-android-programming-the-big-nerd-ranch-guide/ https://bignerdranch.com/blog/gearing-up-for-the-third-edition-of-android-programming-the-big-nerd-ranch-guide/#respond Wed, 07 Sep 2016 10:00:53 +0000 https://nerdranchighq.wpengine.com/blog/gearing-up-for-the-third-edition-of-android-programming-the-big-nerd-ranch-guide/ Today's the day—the schedule is clear, the agenda is set. It's time to start work on a third edition of the Android book.

The post Gearing up for the Third Edition of Android Programming: The Big Nerd Ranch Guide appeared first on Big Nerd Ranch.

]]>

Today’s the day: the schedule is clear, the agenda is set. It’s time to start work on a third edition of our Android book.

How did we get here? If this were a software project, we would be in something close to continuous delivery, continuing to issue new releases. And we do that for the book, too, to a certain degree—our book serves as our course materials, so we do some maintenance in between editions as necessary.

But a book like ours does’t work exactly that way. Not for us, at least. Our goal is to have a physical book on physical shelves all across the world. For as long as that edition is out, that book is our foothold for time on the world of Android development. We’re always aware of where that foothold is, and how it might need to change.

And one morning early this summer, we decided that our list of improvements had become a bit too long.

What Requires a New Edition?

Many changes happen in a new edition, but what really forces a new edition are topics that change the structure of what we lead you through, that alter your priorities as a learner. If we were only changing material at the back of the book, it wouldn’t be worth our effort or your money.

A good example of this is a new tool called ConstraintLayout. Ever since our first edition, we have focused on what we consider the three major layouts in Android: LinearLayout, FrameLayout, and RelativeLayout. GridLayout has been out there for a while, but we didn’t consider it essential enough to integrate into the book. In our view, you will be fine without it.

Not so with ConstraintLayout. ConstraintLayout is a supercharged replacement for RelativeLayout. For many layouts, ConstraintLayout should allow you to replace an entire view hierarchy with a single layout. Reducing the number of nested layouts has a big performance impact, so ConstraintLayout should have a real impact on performance for most apps.

For those reasons, we think that most developers will want to use ConstraintLayout as their go-to layout. That means that it needs to be the go-to layout choice in our book, too. And that’s a much bigger change than writing a new chapter and sticking it in there.

That’s just one example. You get the idea, though—the guide is a whole path, not a set of standalone topics.

What Goes Into a New Edition?

That’s not all that changes, though. With a new edition, we have an opportunity to clean up many smaller details we want to keep up to date:

  • The minSdkVersion. Time has moved on, so we’ll bump up our recommendation again in the third edition.
  • Rewrites. After we’ve had some experience in the classroom, we find that some chapters weren’t as good as we thought they were. The third edition won’t have as much of this as the second, but some chapters have been problematic for readers. They’ll probably be revisited and rewritten.
  • Make it better! This can be so many things, but today I happen to working on improving our solutions so that they’re more up-to-date and easier to import.
  • Address errata. This is also in the “make it better” category, but these are issues readers have reported to us.

We’ve Only Just Begun to Write

This is just the beginning, of course. We’ve got quite a lot of work ahead of us before the new book shows up on shelves. Keep an eye out for more updates!

The post Gearing up for the Third Edition of Android Programming: The Big Nerd Ranch Guide appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/gearing-up-for-the-third-edition-of-android-programming-the-big-nerd-ranch-guide/feed/ 0
Descent Into Databinding https://bignerdranch.com/blog/descent-into-databinding/ https://bignerdranch.com/blog/descent-into-databinding/#respond Sun, 17 Apr 2016 15:00:53 +0000 https://nerdranchighq.wpengine.com/blog/descent-into-databinding/ In this post, I'd like to dive into Data Binding with fresh eyes. I'll explain how Data Binding works at development time and at build time, which should clarify some of the rough edges in the current pre-release version. By the end, I hope to get you to where you can make an informed decision as to whether you want to use Data Binding, as well as how much of it you want to make use of.

The post Descent Into Databinding appeared first on Big Nerd Ranch.

]]>

Last year, the Android team introduced a new building block for Android development: the Data Binding framework.
Data Binding is supposed to eliminate layout-related boilerplate.

If you’re a practicing Android developer, you might not have leaped on this.
Odds are good that you’re already using a view injection library like ButterKnife, which mitigates the pain Data Binding is meant to address.
And Data Binding does so much more than ButterKnife — maybe it’s not worth the additional complexity and mystery of a giant new tool.

In this post, I’d like to dive into Data Binding with fresh eyes.
I’ll explain how Data Binding works at development time and at build time,
which should clarify some of the rough edges in the current pre-release version.
By the end, I hope to get you to where you can make an informed decision as to whether you want to use Data Binding, as well as how much of it you want to make use of.

Some Code To Play With

For the examples in this post, I will use an example app that is familiar to me: CriminalIntent, from our book Android Programming: The Big Nerd Ranch Guide.

If you’re familiar with the book, I am basing my work off of the solution to Chapter 13 in the 2nd edition.
I’ve modified it slightly to illustrate a few ideas here, and to provide some sample data to play with.
If you want to follow along, clone data-binding-talk and checkout the branch jingibus/starting-point.

CriminalIntent is a master-detail app: the main screen (implemented in CrimeListFragment.java shows a list of workplace crimes, each with a few different properties.
If you tap on a crime, the app pulls up a detail screen to edit the crime in question.
We’ll be playing around with Data Binding in the main list screen:

Main CriminalIntent screen

Turning on Data Binding

Make sure you have the latest version of Android Studio 2.

You’ll also need to update your Gradle version.
Newer versions of Android Studio automatically ask you to update to the latest version, so just click “Update” when it asks you.

If for some reason your Gradle version is not automatically updated, you can manually update it to the latest (2.1.0-alpha4 as of this writing) in your top level build.gradle:

dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0-alpha4'

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

Note that this may cause an error saying that you have the wrong Gradle wrapper.
This will go away if you take the error’s suggestion to reimport the project.

Then turn on data binding in your project by adding the following lines to your app/build.gradle:

android {
    compileSdkVersion 21
    buildToolsVersion "20.0.0"

    ...
    dataBinding {
        enabled = true
    }
}

(Feel free to update the build tools and compileSdkVersion if you like — this is a legacy project, so it has older values out of the box.)

This turns on the build integration for the code generation, but it also turns on Data Binding’s IDE integrations.

Switching From a Regular Layout

To use Data Binding with a specific layout, you have to make a couple of modifications to your code.
The first one will be to your layout.
list_item_crime.xml is a big-ish RelativeLayout-based layout file that looks like this (abbreviated for space):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <CheckBox
        android:id="@+id/solved_check_box"
        .../>

    <TextView
        android:id="@+id/title_text_view"
        .../>

    <TextView
        android:id="@+id/date_text_view"
        .../>

</RelativeLayout>

At its heart, Data Binding does one big thing:
it takes a layout file (like list_item_crime.xml) and generates a corresponding Java class, called a binding class.

It will not do this until you tell Data Binding to do its thing, though.
To do that, you wrap your existing layout file in a new tag: <layout>.

<layout>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

        ...

    </RelativeLayout>
</layout>

When you build and deploy to your device, Data Binding will generate an associated class with a similar name, just in CamelCase: ListItemCrimeBinding.

Unlike generated code tools like Dagger, Data Binding does not rely on generated code for type checking.
Instead, it is integrated into Android Studio, so that you do not have to wait through a whole code generation pass to use the fields and methods Data Binding provides.

As of this writing, this integration needs a little jump-start to get going.
To make ListItemCrimeBinding available after adding the <layout> tag, you must restart Android Studio, then rebuild the project.

Once that is done, you can integrate the binding class into your project.
Here, that will be in your ViewHolder implementation, CrimeListFragment.CrimeHolder:

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {

    private final ListItemCrimeBinding mBinding;
    private TextView mTitleTextView;
    private TextView mDateTextView;
    private CheckBox mSolvedCheckBox;

    private Crime mCrime;

    public CrimeHolder(ListItemCrimeBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
        itemView.setOnClickListener(this);

        ...
    }

Instead of taking in a View, CrimeHolder now takes in ListItemCrimeBinding, which has the View as its root.

To create the instance of ListItemCrimeBinding itself, you use DataBindingUtil instead of directly calling LayoutInflater.inflate.
In this case, inflation happens inside CrimeListFragment.CrimeAdapter.onCreateViewHolder.
The Data Binding version of that code looks like this:

private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {

    private List<Crime> mCrimes;

    public CrimeAdapter(List<Crime> crimes) {
        mCrimes = crimes;
    }

    @Override
    public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
        ListItemCrimeBinding binding = DataBindingUtil
                .inflate(layoutInflater, R.layout.list_item_crime, parent, false);
        return new CrimeHolder(binding);
    }

With that, you’re using Data Binding.

Great.
Now what?

View Binding

The moment you add a <layout> tag and generate a binding class, Data Binding is doing useful work.
All generated binding classes have a camelCase generated field for each android:id that is assigned in the layout file.
When the layout is inflated, those views are extracted and bound to their associated fields.

That means that the moment you have an instance of ListItemBinding, you can immediately ditch all of your findViewById related code.
All of the fields and findViewById calls currently in CrimeHolder can be removed:

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {

    private final ListItemCrimeBinding mBinding;

    private Crime mCrime;

    public CrimeHolder(ListItemCrimeBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
        itemView.setOnClickListener(this);
    }

    public void bindCrime(Crime crime) {
        mCrime = crime;
        mBinding.titleTextView.setText(mCrime.getTitle());
        mBinding.dateTextView.setText(mCrime.getDate().toString());
        mBinding.solvedCheckBox.setChecked(mCrime.isSolved());
    }

Pretty handy.

IDE Integration

If you are like me, and you know that this framework generates code, you will want to try and look at the code by doing something like this:

Navigate to bound view

If you do, you may be surprised (like I was).
Instead of taking you to the generated code, it takes you to the layout file.

The reason this happens is the same reason navigating to the declaration of R.layout.list_item_crime takes you to the layout file instead of the generated R.java file:
the IDE integration does error checking without the generated code, so that you do not have to rebuild to use a generated field.
If you already know Android Studio inside and out, this is very confusing.
In the long run the IDE integration should have the same benefits that the resource ID integration has.

Note that none of this applies if you’re building outside of Android Studio, where it will behave identically to a tool like Dagger.
It’s also possible to use apt to skirt around the IDE integration and force the behavior of a typical generated code library.
I don’t recommend it, though — it may feel more familiar initially, but it will be slower in the long run.

Binding Expressions

Data Binding also gives you the ability to use binding expressions.
Most of this article will be concerned with binding expressions in some way.

The basic idea of a binding expression is small: a binding expression is an expression in an XML attribute that tells your binding class to set a value on a view object.
Binding expressions are very close to Java expressions, plus some additional syntactic sugar, like resource references, to simplify writing view logic.

Binding expressions are particularly handy when you want to assign a value that relies on an existing value in some way, but doesn’t really deserve its own name.
For example, in this version of CriminalIntent, we have the following value assigned to solved_check_box:

android:padding="@dimen/list_item_padding_2x"/>

As you might imagine, list_item_padding_2x is twice the value of list_item_padding.
You can use a binding expression to instead write it this way:

android:padding="@{@dimen/list_item_padding * 2}"/>

Note the special @{} binding mustache syntax.
This signals that the attribute will be processed by Android Data Binding.
Data Binding will read the expression as it generates the binding class, and generate code to assign this value at runtime.
The XML attribute itself is stripped out of the XML that ships with your app.

Simple Data Binding: No Data Section

Before I continue, I want to mark this down as a potential stopping point for you in your own work.
Admittedly, it’s a bit like heading out on the Oregon Trail from Missouri and stopping in Nebraska.
But there’s a lot to be said for Nebraska, and it’s a long haul to the other side of the Rockies.

So here we are: you can use Data Binding for view binding and basic binding expressions, and stop there.
Any further usage of Data Binding requires a <data> section (described below).
If you disallow the use of this section in code review, you limit your usage of the tool to these two mechanisms.
If you’re interested in seeing a project at this level of integration, checkout the jingibus/no-data-section branch of data-binding-talk.

The biggest reason I like this stopping point is view binding.
View binding dramatically reduces the number of times you have to refer to view names in your code.

Take the name title text view from above as an example.
Without data binding, you have at least 5 places title text view must appear in some form:

  • @+id/title_text_view in the layout file
  • R.id.title_text_view in the call to findViewById
  • mTitleTextView in the field definition
  • mTitleTextView in the assignment when findViewById is called
  • Finally, mTitleTextView when you actually use the widget

ButterKnife cuts that down to four:

  • @+id/title_text_view in the layout file
  • R.id.title_text_view in your @Bind annotation
  • mTitleTextView in the field definition
  • mTitleTextView when you use the widget

If you use Data Binding instead, that’s further cut in half:

  • @+id/title_text_view in the layout file
  • titleTextView when you use the widget

I think that’s a pretty clear win.
The only other tool I know of that competes with Data Binding’s view binding is Kotlin’s Android view extensions,
and that requires you to migrate to Kotlin.
I love Kotlin, but that’s a much bigger change than Data Binding is.

Data Variables

While the layout tag enrolls you into the cult of Data Binding, delving into its mysteries requires a new element in your XML file: <data>.
This section goes right after your initial layout tag:

<layout>
    <data>

    </data>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        ...

In the data section, you can define variables on the layout.
This gives you the ability to send objects into the layout file.
So you could define a variable for the crime’s title:

<layout>
    <data>
        <variable
            name="crime"
            type="com.bignerdranch.android.criminalintent.Crime"/>
    </data>

Defining a variable in the data section creates a property on the associated binding class.
So if you jump back over to CrimeListFragment, you will see that ListItemCrimeBinding now has a setCrime setter:

public void bindCrime(Crime crime) {
    mCrime = crime;

    mBinding.setCrime(mCrime);
    mBinding.titleTextView.setText(mCrime.getTitle());
    mBinding.dateTextView.setText(mCrime.getDate().toString());
    mBinding.solvedCheckBox.setChecked(mCrime.isSolved());
}

Once you have defined a variable, you can use it within binding expressions.
For example, you could wire up dateTextView, including a formatting message:

<TextView
    android:id="@+id/date_text_view"
    android:text="@{`Date discovered: ` + crime.getDate().toString()}"
    .../>

(Inside of binding expressions, backticks are interpreted as double quotes. Handy.)

Almost everything that you can write in a Java expression is valid in a data binding expression:
boolean logic, comparisons, ternary logic operators, and so forth.
You cannot use this, super, or new, and you cannot explicitly invoke generics, but everything else is available to you.

Formatting Strings

Of course, you would not want to use a string literal in your Java code.
You would want to use a formatting string instead.

<resources>
    ...
    <string name="hide_subtitle">Hide Subtitle</string>
    <string name="subtitle_format">%1$s crimes</string>
    <string name="list_date_format">Date discovered: %1$s</string>
</resources>

You can use formatting strings in binding expressions as functions, passing in the formatting parameters.

<TextView
    android:id="@+id/date_text_view"
    android:text="@{@string/list_date_format(viewModel.getDate().toString())}"
    .../>

Listeners & Lambdas

Android Studio 2.0 beta 6 also introduced a lambda syntax for hooking up event listeners and other callbacks in binding code.
CrimeHolder is responsible for handling view clicks.
If you add it as a variable:

<layout>
    <data>
        <variable
            name="crime"
            type="com.bignerdranch.android.criminalintent.Crime" />
        <variable
            name="holder"
            type="com.bignerdranch.android.criminalintent.CrimeListFragment.CrimeHolder"/>
    </data>
    ...

And assign it inside of CrimeListFragment.CrimeHolder:

public CrimeHolder(ListItemCrimeBinding binding) {
    super(binding.getRoot());
    mBinding = binding;
    mBinding.setHolder(this);
}

You can wire up the call to holder.onClick directly in the layout file.

<layout>
    <data>
        <variable
            name="crime"
            type="com.bignerdranch.android.criminalintent.Crime" />
        <variable
            name="holder"
            type="com.bignerdranch.android.criminalintent.CrimeListFragment.CrimeHolder"/>
    </data>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:onClick="@{(view) -> holder.onClick(view)}"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

The expression (view) -> holder.onClick(view) is a lambda expression.
Data Binding lambda expressions are a limited, abbreviated version of Java 8 lambdas:
no types are permitted in the variable names, and no code blocks are permitted in the body of the lambda.
You can’t leave off the parens, but you can omit the variable name if you aren’t using it.

At runtime, the lambda will behave as if you had defined a complete listener implementation:

new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        holder.onClick(view);
    }
};

Mechanics of View Binding

So how does it work?
When are binding expressions run?
How they triggered?
I could talk about it, but I’d rather show you.

As mentioned before, Data Binding is integrated into the IDE, so it does not always generate code.
You can force code to be generated, though.
Run your project, and if your build and deploy is successful, you should have generated code.
Switch to the Project view in your Project tab in Android Studio.

The generated code goes in app/build/intermediates/classes/debug (or classes/release, for a release build).
Deep inside classes/debug, in com/bignerdranch/android/criminalintent/databinding, you will find the generated ListItemCrimeBinding.java class.

Navigating to generated code

Double click to crack it open.

We know that the views must be repopulated when you call setCrime.
Here’s what setCrime looks like as of this writing (no guarantees it will stay the same):

public void setCrime(com.bignerdranch.android.criminalintent.Crime crime) {
    this.mCrime = crime;
    synchronized(this) {
        mDirtyFlags |= 0x2L;
    }
    super.requestRebind();
}

Each individual source value that can be displayed has its own dirty bit in mDirtyFlags.
When that source value changes, the dirty bit is flipped and a rebind is requested.

What does it mean to request a rebind? Well, here’s what requestRebind() looks like:

protected void requestRebind() {
    synchronized (this) {
        if (mPendingRebind) {
            return;
        }
        mPendingRebind = true;
    }
    if (USE_CHOREOGRAPHER) {
        mChoreographer.postFrameCallback(mFrameCallback);
    } else {
        mUIThreadHandler.post(mRebindRunnable);
    }

}

Requesting a rebind simply posts a callback to be run on the main thread as soon as possible.
So you can assign new values to three different variables, and it will only run the bindings one time.
(The actual method on ListItemCrimeBinding that assigns the values is called executeBindings(). We’ll check that out in a minute.)

This is a problem in a RecyclerView, though.
A RecyclerView could be scrolling very quickly.
If rebinding does not happen immediately, there is a possibility of visible flicker.

There is a straightforward fix, thankfully.
If you need the bindings executed immediately, just call executePendingBindings() on your binding class.

public void bindCrime(Crime crime) {
    mCrime = crime;

    mBinding.setCrime(crime);
    mBinding.executePendingBindings();
}

This will run the code for all of your binding expressions, and unset the dirty bits for all of the variables you changed.

Syntactic Goodies

As was mentioned above, binding expressions are not only Java expressions.
They’re intended to write small bits of code to wire up views.
To make that easier, binding expressions have some additional syntax and semantics differences that make it easier to write brief view logic.

Null Receivers

The first semantic difference is that binding expressions generate code that treats null differently the regular Java code.
If you dive into ListItemCrimeBindings.executeBindings(), you will find a lot of code like this:

if (crime != null) {
    // read crime~~getDate~crime~
    crimeGetDateCrime = crime.getDate();
}

if (crimeGetDateCrime != null) {
    // read crime~~getDate~crime~~toString~crime~~getDate~crime~
    crimeGetDateCrimeToS = crimeGetDateCrime.toString();
}

No methods are ever called without first verifying that the method’s recipient is not null.
The effect of this is that null valued objects behave as if they receive messages, like in Obj-C.
Method invocations on null run no code, and always yield default values.

This preserves some existing Java behavior, while making it more resilient to the presence of null.
Take the example from earlier, where you used string concatenation:

"@{`Date discovered: ` + crime.getDate().toString()}"

If crime or crime.getDate() is null here, the entire crime.getDate().toString() expression ends up being null.
Since Java string concatenation converts null values to the string "null",
the whole expression evaluates to "Date discovered: null.

This behavior prevents many NullPointerExceptions, but not all.
null receivers may send null values on to other methods.
For example, if you wrote the following:

"Date discovered: ".concat(crime.getDate().toString())

concat throws a NullPointerException if it receives a null parameter.
So you’d get an NPE if crime were null.

Null Coalescing

Another way binding expressions help abbreviate null handling is through the null coalescing operator, ??.
The null coalescing operator helps when you want to provide a default value when some other value is null.
Take this for example:

"@{@string/list_date_format(crime.getDate().toString() ?? `(no date)`)}"

Instead of showing "Date discovered: null" for a null value, you would see "Date discovered: (no date)".

Properties

You also don’t have to write out property getters.
So instead of:

"@{@string/list_date_format(crime.getDate().toString() ?? `(no date)`)}"

You can write:

"@{@string/list_date_format(crime.date.toString() ?? `(no date)`)}"

This works for other standard getter conventions like isSolved(), too.

Gotchas

There are a couple of gotchas to keep in mind with Data Binding right now.
How temporary these are is unknown, but for now they are good to know about.
(They shed some light on things worth knowing, too.)

Layout Attributes

Remember that earlier I said that setting a binding expression on an attribute causes that attribute to be processed by Data Binding, not by the XML layout inflation process.
And as we have seen, binding expressions are all about setting values on view objects.

What happens when you set a binding expression on a layout parameter, though?
You might want to do the same trick with margins that you used on padding:

...
<TextView
    android:id="@+id/date_text_view"
    android:text="@{@string/list_date_format(crime.date.toString() ?? `(no date)`)}"
    android:layout_margin="@{@dimen/list_item_padding * 2}"
    .../>
...

This will throw an error:

~/src/android/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
Error:(38, 38) Cannot find the setter for attribute 'android:layout_margin' 
with parameter type float.

This is because, as of right now, data binding is a mechanism for setting values on view objects.
That means that under the hood, all of your attribute setting is being converted to method calls.
Which is what the error is complaining about: it cannot find a setter for a property named layout_margin.
More about that in a moment.

Generated Code Errors

In current versions of Data Binding, XML errors can be confusing.
For example, if you wrote the following in your XML file:

...
<TextView
    android:id="@+id/date_text_view"
    android:text="@{@string/list_date_format(crime.date.toString() ?? `(no date)`)}"
    android:allCaps="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_toLeftOf="@id/solved_check_box"
    android:layout_below="@id/title_text_view"
    tools:text="Crime Date"
    android:padding="@{@dimen/list_item_padding * 2}"/>
...

The attribute android:allCaps does not exist — it’s actually android:textAllCaps.
If you try to run the app, you get the following error:

~/src/android/CriminalIntent/app/build/intermediates/data-binding-layout-out/debug/layout/list_item_crime.xml
Error:(35) No resource identifier found for attribute 'allCaps' in package 'android'

So you say, “D’oh,” as is the fashion at this time, and double click on the error to go fix it.
That will pop you into this code:

...
<TextView
    android:id="@+id/date_text_view"
    android:tag="binding_3"
    android:allCaps="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_toLeftOf="@id/solved_check_box"
    android:layout_below="@id/title_text_view"
    tools:text="Crime Date"
...

At first glance, this looks like the same place you just were.
You might even fix the offending line here and rebuild.

It won’t work though, because this is actually a post-processing version of the file.
Remember above when I said that XML attributes are stripped out to be processed by the data binding tool?
If you look closely, you will see that this listing is actually missing two attributes:
android:text and android:padding.
This is your layout XML after those attributes are stripped.
Those attributes were assigned with binding expressions, and so the code for them is in ListItemCrimeBinding.

If you run into this problem, it means that you will have to manually reopen the layout file in res/layout.
If you do not, you will get into a loop where you think you have fixed the problem, and then find the error you fixed pops up again immediately.
I have found myself going through a couple of cycles of this, even though I know about it.
But hey — at least I know.

Attribute Processing

Strangely enough, you actually can get that non-existent android:allCaps attribute to work.
All you have to do is change its assignment to use a binding expression.

...
<TextView
    android:id="@+id/date_text_view"
    android:text="@{`Date discovered: ` + crime.date.toString() ?? `(no date)`}"
    android:allCaps="@{true}"
    .../>
...

Remember that a binding expression says, “Data Binding, please process this attribute assignment for me.”
The default way Data Binding processes an attribute is to look for a single-parameter setter on the view with the same type as the binding expression.
And, as it turns out, the setter associated with the android:textAllCaps attribute is called setAllCaps(boolean).
So android:allCaps works perfectly fine.

This is what’s called an automatic setter.
If a setter exists on a view, you can always set it by using a binding expression with the appropriate type.

Non-standard Attributes

Some widget properties you would want to assign aren’t single-parameter setters, though.
And some attributes, like android:textAllCaps, don’t have the same name as their setters.

For example, say you had an EditText that you wanted to setup a text listener for.
There is no documented way to configure this interface in XML.
In Java, you would call addTextChangedListener with an implementation of the TextWatcher interface:

public interface TextWatcher extends NoCopySpan {
    void beforeTextChanged(CharSequence var1, int var2, int var3, int var4);
    void onTextChanged(CharSequence var1, int var2, int var3, int var4);
    void afterTextChanged(Editable var1);
}

Can you use it with Data Binding?
If so, how do you find out how it works?

Fantastic BindingAdapters And Where To Find Them

There is not yet any official documentation, but there is still a way to find these attributes.
All custom attribute behavior is implemented through binding adapters.
If you need to know what custom attributes apply to a kind of widget, all you need to do is find its binding adapters and you will see all the custom attributes.

The binding adapters are written in a class with the widget’s class name, plus Adapter on the end.
So for TextView, you can find the binding adapters by going to Navigate->Open class in Android Studio (or Cmd-O, if you’re using the same keybindings as me).
Type in TextViewBindingAdapter, and open up the class that it finds.

At the top, you’ll initially see this:

@BindingMethods({
        @BindingMethod(type = TextView.class,
                       attribute = "android:autoLink",
                       method = "setAutoLinkMask"),
        ...
        @BindingMethod(type = TextView.class,
                       attribute = "android:onEditorAction",
                       method = "setOnEditorActionListener"),
})
public class TextViewBindingAdapter {
    ...

For attributes like android:textAllCaps, where the method name does not match up with the attribute name, a BindingMethod attribute points Data Binding in the right direction.

This isn’t the case for TextWatcher, so you will not find what you need in this section.
Type Cmd-F to do a search in this file, and look for TextWatcher.
You should jump right to this method:

...
@BindingAdapter(value = {"android:beforeTextChanged",
                         "android:onTextChanged",
                         "android:afterTextChanged"},
                requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
        final OnTextChanged on, final AfterTextChanged after) {
    ...

Paydirt.
This is what a binding adapter implementation looks like.
It is an annotated static method, which may handle one or more attributes.

The first parameter of the method is always the kind of view the binding adapter applies to.
Following that is one parameter for each attribute handled by the adapter.
This adapter handles three attributes, one for each method on the TextWatcher interface.
The parameters correspond, in order, to the list of attribute names specified in the @BindingAdapter annotation’s value parameter.

Now you know how to configure a TextWatcher callback.
OnTextChanged has one method, onTextChanged, which takes in four parameters: a String, and three ints.

public interface OnTextChanged {
    void onTextChanged(CharSequence s, int start, int before, int count);
}

So to listen to TextWatcher.onTextChanged(), you would write the following binding expression in your layout file:

android:onTextChanged="@{(s, start, before, count) -> holder.onTitleTextChanged()}"

Lambdas let you omit the callback parameters, too, if you aren’t using them.
So you could also write your callback like so:

android:onTextChanged="@{() -> holder.onTitleTextChanged()}"

Writing Your Own Attributes

If you need an attribute that is not provided, you can write your own binding adapters.
Complete coverage of that topic is a little beyond the scope of this article, but the source for TextViewBindingAdapter above is a great place to start.
The Data Binding Guide also explains this topic in some detail.

One Last Thing: ViewModel

I’ve shown you how to use Data Binding to pull values from model objects into your layout automagically.
Unfortunately, I’ve also made a mess: my layout is integrating code from a few different areas.
That puts a lot of real responsibility in the layout file.

That’s a bad thing.
Responsibility means you need to provide oversight.
Oversight means code review and tests.
You don’t want to code review your layout files in this way — the functionality will disappear amongst all the display concerns.
And you definitely don’t want to put it under test.

So the last bit of cleanup work to do here is to take the interaction and data formatting pieces out of the layout file and into a new class that will take on that responsibility: a view model.

I create my class, and call it CrimeListItemViewModel, since its responsibility is to the crime list item.
I add a couple of dependencies:

public class CrimeListItemViewModel {
    private final Context mContext;
    private Crime mCrime;

    public CrimeListItemViewModel(Context context) {

        mContext = context.getApplicationContext();
    }

    public Crime getCrime() {
        return mCrime;
    }

    public void setCrime(Crime crime) {
        mCrime = crime;
    }
}

This view model is showing information about a Crime, so it definitely needs a Crime.
It will start a new activity when it is selected, too, so it needs a Context.

Then you expose getters and event trigger methods:

    ...
    public CrimeListItemViewModel(Context context) {
        mContext = context;
    }

    public Crime getCrime() {
        return mCrime;
    }

    public void setCrime(Crime crime) {
        mCrime = crime;
    }

    public String getTitle() {
        return mCrime.getTitle();
    }

    public String getRenderedDate() {
        return mCrime.getDate().toString();
    }

    public boolean isSolved() {
        return mCrime.isSolved();
    }

    public void onCrimeSelected() {
        Intent intent = CrimePagerActivity.newIntent(mContext, mCrime.getId());
        mContext.startActivity(intent);
    }
}

Update the layout file to use an instance of CrimeListItemViewModel instead of Crime and CrimeHolder.

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="viewModel"
            type="com.bignerdranch.android.criminalintent.CrimeListItemViewModel" />
    </data>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:onClick="@{() -> viewModel.onCrimeSelected()}"
                    ...>

        <CheckBox
            android:id="@+id/solved_check_box"
            android:checked="@{viewModel.solved}"
            .../>

        <TextView
            android:id="@+id/title_text_view"
            android:text="@{viewModel.title}"
            .../>

        <TextView
            android:id="@+id/date_text_view"
            android:text="@{`Date discovered: ` + viewModel.renderedDate ?? `(no date)`}"
            ...
            android:padding="@{@dimen/list_item_padding * 2}"/>

    </RelativeLayout>
</layout>

Then change your CrimeHolder to use the view model, too.

public class CrimeHolder extends RecyclerView.ViewHolder {
    private final ListItemCrimeBinding mBinding;
    private final CrimeListItemViewModel mViewModel;

    public CrimeHolder(ListItemCrimeBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
        mViewModel = new CrimeListItemViewModel(getActivity());
        mBinding.setViewModel(mViewModel);
    }

    public void bindCrime(Crime crime) {
        mViewModel.setCrime(crime);
        mBinding.executePendingBindings();
    }
}

This idea of using a view model class is often called “model-view-viewmodel”, or MVVM.
This is not a complete coverage of the idea of MVVM architecture.
But it does show the essentials of the idea.

Observable Objects

Look great, right?
Not if you scroll down, it’s not:

borked list of crimes

The list items are never getting updated with the new information being sent to ListItemCrimeViewModel.
And how could they? The binding object doesn’t know.

One fix is to add a call to ListItemCrimeBinding.invalidateAll(), which will trigger a rebind of everything.
Far less brittle, though, is to use an observable object instead.

A observable Data Binding object implements the android.databinding.Observable interface.
This is a different from RxJava’s Observable interface: a Data Binding Observable exposes the ability to listen to changes on an individual properties of an object.

Implementing Observable requires writing plumbing to hook up listeners to each individual property.
Rather than writing this yourself, you are almost always better off extending BaseObservable instead:

public class CrimeListItemViewModel extends BaseObservable {
    ...

You then need to specify which properties of your class may be bound to by annotating them with @Bindable:

    ...
    @Bindable
    public String getTitle() {
        return mCrime.getTitle();
    }

    @Bindable
    public String getRenderedDate() {
        return mCrime.getDate().toString();
    }

    @Bindable
    public boolean isSolved() {
        return mCrime.isSolved();
    }
    ...

The last step is to notify whoever is observing your object when one of these properties changes.

Typically, this is done by calling notifyPropertyChanged(int).
The int is a special constant for each property name in a file called BR.java.

BR.java is a generated file similar to R.java.
Instead of containing resource IDs, BR.java contains binding resource IDs — integer constants that can be used to identify properties by name instead of Strings.
When you mark a field or getter with @Bindable, a matching constant with the same name is added to BR.java.
So to say that getTitle()’s value has changed, you would call:

notifyPropertyChanged(BR.title);

In CrimeListViewModel, the only thing that triggers property changes is a call to setCrime(Crime), and that triggers changes to all the properties.
Which you can signify by just calling notifyChange():

    public void setCrime(Crime crime) {
        mCrime = crime;
        notifyChange();
    }

This signals that all of the object’s properties have changed.

Non-simple Data Binding: MVVM

It was quite a lot of ground to cover, but now you’re at the other end of the trail.
(If you check out the code from the repo, this is what you see on the master branch.)
If you want to use Data Binding to its fullest potential, I recommend going all the way here.
Stopping without going all the way to a View Model architecture is like stopping in east Oregon — Portland is almost there!
And there are food trucks there!

Seriously, though, there are some very nice things about this architecture, particularly if you’re doing testing.

  • View models are easy to test.
    Click handling is tested by invoking a method and seeing what happens.
    Property values are verified by calling input data setters and seeing what the output properties show.
  • Fewer ids, fewer names.
    Views that are filled in with data binding don’t need to be referenced in code, so they don’t need ids.
    If our example code didn’t use RelativeLayout, it would not need any ids at all.
  • You can read the layout file and see what data goes where, rather than inferring it from the name of each component.

Not everyone will find it worthwhile to make the migration to this full-blown usage of data binding.
If you don’t, I recommend stopping at the simple usage described above in the “Simple data binding” section.

In Conclusion: Opinions

Life is easier without them, but I have a hard time writing conclusions without sharing an opinion.
So here’s some short and sweet spit takes:

My first one might be a bit controversial: if you’re using apt with Data Binding, I recommend stopping.
It’s only slowing you down.
Relying on the IDE integration will make it easier for you to navigate your codebase, and eliminate the need to rebuild to make fields visible.

If you can pair MVVM with Binding, I recommend doing so.
We’ve already used it in some production apps, to good effect.
The initial hurdles can be a little painful, but it really kills a lot of annoying boilerplate in your Java code.

If you cannot use a View Model architecture, I still recommend using Data Binding, but only as a view binding tool.
Without MVVM or a similar discipline governing what kinds of objects you use in your layout file,
your layout files can easily become a locus of maintenance nightmares.
Debugging is hard enough without having to add your resources folder to the places you need to hunt down business logic.

So don’t go halfsies on this.
There may be other sweet spots for Data Binding usage.
MVVM and view binding are the two I can recommend today, though.

And I think that’s about all I have to say about Data Binding.

The post Descent Into Databinding appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/descent-into-databinding/feed/ 0
Event buses, NSNotificationCenter and @channels https://bignerdranch.com/blog/event-buses-nsnotificationcenter-and-channels/ https://bignerdranch.com/blog/event-buses-nsnotificationcenter-and-channels/#respond Thu, 21 Jan 2016 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/event-buses-nsnotificationcenter-and-channels/ There are good reasons people like event buses. Why have people come to hate them?

The post Event buses, NSNotificationCenter and @channels appeared first on Big Nerd Ranch.

]]>

This is a post about mobile app development. Before I get to that, though, I have a little beef I need to talk about.

Big Nerd Ranch, like many other companies, uses Slack. Slack is a well-done glorified chat app where you join or create channels and chat with people, or you can message folks directly if you so choose.

One of the things I like the most about Slack is its well-thought-out notification system. Normal channel messages are highlighted when I switch to the app or open it up, so that I can catch up on what I haven’t read. If someone messages me directly, though, or calls me out by name in one of the channels I’m lurking in by saying @bphi, I get a little sound and a pop up notification on my computer. If I’m not at my computer at that moment, I’ll even get an e-mail.

You can tweak all these things, but that’s the basic idea. Notifications interrupt me, while everything else doesn’t.

The Infamous @channel

This brings us to the @channel. When a user types @channel, Slack sends a notification to every single person in a particular room, waking them up to go read what you have to say.

As you can imagine, @channels are controversial. Some folks rely on them heavily, because they rarely interact with Slack at all. They are focused on their work, and don’t keep a chat app open. These folks don’t relish the idea of opening up Slack, asking their question and then waiting for a response. Using an @channel allows them to open Slack, ask a question and get an immediate response. It just works.

Not necessarily for other folks, though. The many folks who were briefly interrupted probably will not say anything in response to an offhanded, unnecessary @channel. They will grumble briefly to themselves and return back to their work. Some people may curse aloud at their desk, or run off and write a blog post (cough). More likely than not, these folks will just turn off @channel notifications entirely.

Should you turn them off, though? Is that a good idea?

Technical Problems and Social Problems

I think that we underestimate the degree to which technical problems correspond to social and cultural problems. Sure, we talk about Conway’s law in our tongue-in-cheek kind of way, but rarely do we actually talk about how we might better change our code by changing how we interact with one another.

I think we can even consider our own programs to be like little snowglobe societies. (I don’t think I’m the only one, either.) The single responsibility principle? It’s the programmer’s version of the golden rule. And if we’re inconsistent people, we’re going to make inconsistent little reflections of ourselves. And the same thing applies to how our code fits in with our peers’ code—if we’re not sure how we fit into our team, our code won’t know how it fits in, either.

This problem with @channels does look a bit like a technical problem—it’s a mechanism that just isn’t useful. Anyone can abuse it and interrupt everybody, and they probably will. So get rid of it.

But it’s partly a social problem, too. Not every organization will respond in the same way to the presence of the @channel. Some organizations will be filled with people using them for everything, everyone interrupting one another; in others, they will serve exactly their intended purpose, only being used to wake up listeners for an important event. Perhaps some organizations are better served by interrupting everyone. I doubt it, but I don’t know everything.

Event Buses

So what does this have to do with mobile app development? Recall that I was supposed to be talking about event buses and NSNotificationCenter. These are tools for two different platforms, each of which works in a slightly different way. They usually fulfill similar roles on your app’s architecture, though.

I’m an Android guy, so let’s talk about the event bus first. Here’s what an event bus is: an object that you can subscribe to events on. So you might write the following code in a Fragment:

public class WaiterFragment extends Fragment {
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEventBus.subscribe(this);
    }

    public void onEvent(FireAlarmEvent event) {
        fleeInTerror();
    }

    ...
}

And then off in your FireDetector, you might fire the event.

public class FireDetector {
    private EventBus mEventBus;

    public FireDetector(EventBus eventBus) {
        mEventBus = eventBus;
    }

    ...

    public fireDetected() {
        mEventBus.send(new FireAlarmEvent());
    }
}

This will call onEvent(FireAlarmEvent) for everyone who is subscribed to that particular event.

On iOS, it’s a similar story. Events are identified by well-known string constants and propagated through an NSNotificationCenter.

class Villager {
    func fleeInTerror() {
        /* flee */
    }

    init() {
        prepareToFlee()
    }

    deinit {
        removeAllObservers()
    }

    func prepareToFlee() {
        let notificationCenter = NSNotificationCenter.defaultCenter()
        let observer = notificationCenter.addObserverForName(
            FireAlarmDidSound, object: nil,
            queue: NSOperationQueue.mainQueue()) {
                [weak self]
                _ in self?.fleeInTerror()
        }

        observers.append(observer)
    }

    private var observers = [AnyObject]()
    private func removeAllObservers() {
        let notificationCenter = NSNotificationCenter.defaultCenter()
        for o in observers { notificationCenter.removeObserver(o) }
    }
}

You can post the event like so:

let FireAlarmDidSound = "FireAlarmDidSound"

class FireDetector {
    func didDetectFire() {
        let notificationCenter = NSNotificationCenter.defaultCenter()
        notificationCenter.postNotificationName(
            FireAlarmDidSound, object: nil)
    }
}

Android has a communication problem, to put it mildly. In lecture, I have been known to describe Android as “a place where any one of your friends might die or come back to life at any time.” It is a framework where the operating system is in charge of a lot of important lifecycles, and you are not.

As a result, it can be a hassle to hook up point-to-point communication between different components in the system. A lot of this is a good thing—communicating directly from one Activity to another is a bad idea, whether it is difficult or not.

It’s not just Android, though. Any client app usually ends up having this problem to some degree. Screen controllers tend to either spawn each other in long chains, or be spawned by an overlord of some kind. When several of them need to be informed of the same event, you need to wire an observer.

As these observers proliferate, your controllers get cluttered with code finding the place a specific event is coming up, wiring up a listener to it and making sure to unsubscribe when you’re done. An event bus or notification center solves that problem.

Why Event Buses Can Be Horrible

So there are good reasons people like event buses. Why have people come to hate them? In short, event buses can be abused to create spaghetti code.

Say that you have written an restaurant simulator app that features an event bus. Your app has a HostessStandFragment, where your user walks up to the hostess stand and tries to get a table. In your next new feature, HostessStandFragment gets a new button that allows diners to place to-go orders.

The hostess doesn’t make the food, of course. The kitchen makes the food, and the patrons order it from waiters. Except now the hostesses are ordering food, too.

The event bus provides an immediate solution to this problem: all you have to do is create an event class, subscribe to it in the recipient and send it in the sender—bam, you’re connected. If you need to pass anything along, just include it in your event object. On iOS, you would include a payload of some kind.

In the case I showed here, that is all well and good. In other cases, it can cause problems. If you have an event bus, you can connect any point A to any point B, as long as both points have a bus handy. Two views can easily update one another, if both have a bus handy.

The problem is surprisingly similar to the one caused by gotos: one could always build well-designed programs off of gotos, but one developer with a burning personal need could always jump through the walls you lovingly constructed with a couple of lines of code. With the goto, this misuse broke your ability to think about the line-by-line sequencing of your code. With the event bus, such misuses break your ability to think about the lattice of responsibilities your classes represent.

The negative results are much different than they are with @channels. Instead of interrupting everyone, control flow becomes confused, jumping randomly from class to class to follow the immediate needs of your code. Instead of being localized to one area of your app, you end up having to follow the thread all over the place to figure out how everything works. And when one event fires another even? Ooh, man.

A Social View of Events

The @channel problem made me think about this in another way, though. As one person, there’s only so much I can do about @channels. That limitation shapes my thinking on how I can really make things better for the people around me, not just myself.

As an engineer, I don’t like it when something is a social problem, because I can’t impose my own solution. Even if I’m the boss, I don’t get to choose what other people do. I can’t make people believe in me if they don’t. They’re people just like I am, and they do whatever the hell they want to. I can rant about @channels all day, but it won’t do a lick of good for anybody if my colleague still finds the @channel useful and either isn’t concerned about, or isn’t aware of the negative impact it might have on others.

Social problems aren’t amenable to quick fixes. I have to trust my colleagues to fix my problem for me, as if my problem were theirs. That trust is powerful, but it takes time and patience to build it up. And you might break it down again if you go on too many angry rants.

So when I’m dealing with my colleagues, the rule of thumb is simple: appreciate their humanity, and that we are all doing our best to do what we believe is good. For a small thing like @channels, kindly making my colleagues aware of the effect their actions have on me is probably all I should do, unless I’m in a position of some influence.

If you think of your objects from a social perspective, posting an event to accomplish something your object cares about is a severe boundary violation. It may be convenient, but it ends up being corrosive to the cohesiveness of your design because your object is divesting itself of its responsibilities in a thoughtless manner.

Typical Solutions

This is not an abstract issue. I’ve run into event bus spaghetti code in my own work, and it’s not pretty. My iOS colleagues have the same issues with NSNotificationCenter.

So how can this spaghetti code be avoided? I’ve seen or been tempted by two tactics:

  1. Just don’t use an event bus at all. This is the goto solution: it does bad things, it’s bad, get rid of it. The assumption here is that anything you can do with an event bus can be better accomplished with some other construct, even if that means writing a lot more code.

  2. Enforce some kind of narrow, codified design restriction on how the bus is used. For example, “Only post events from long-lived components in the model layer, and only subscribe to them from within the controller layer.” The rule would be clear and unambiguous. The hope is that this establishes clear lines of responsibility, and eliminates the horrible nested event triggering problem.

I think solution #1 is valid, but I don’t like it. I like having this tool in my toolbox. Solution #2 makes sense, but I think there’s a deeper idea at work.

A Rule of Thumb

So: when should you post an event? When should you send an @channel?

Here’s my proposed rule for both situations: You should break into someone else’s context only if it is your legitimate responsibility to them to do so.

Every time you define an event or notification, every time you use an @channel, you pass the buck to someone else. With a normal point-to-point observation, you are performing either a hierarchical or peer handoff of responsibility.

With a broadcast observation, however, you perform a communal handoff of responsibility: like a fire alarm, you pass the buck to someone, probably multiple someones, and you do it in a way that is by design open to all. Importantly, your counterparty is not expecting anything from you specifically.

This means two things: it matters what your responsibility is, and it matters what their responsibility is. You need to have a responsibility to let a crowd of receivers know; they need to have a responsibility to take some action on that event. Without both of those in play, you will want to use a different tool.

Bad Uses

In Android, the most common misuse is in the case where creating a more appropriate hookup is just a hassle.

Here’s an example. Big Nerd Ranch has an app that handles conference room bookings. New bookings are created by a Service object behind the scenes. To inform the front end that a new booking is available, an event is fired.

Does everyone have a responsibility to act on a room booking? Well… no, not really. Only specific parts of the front end do. The main reason this event exists is that the right solution is to create a separate component to send the event through, and creating a separate component is a pain.

The shortcuts can get even worse than that. The same app has an event named DismissInfoDialogEvent. This event is fired to signify that a DialogFragment was dismissed. In all likelihood, the only other object responsible for acting on that lifecycle event is the object that showed it in the first place. This is a bad use.

The @channel operates in a far more concrete space than the event or the notification, so this should be more clear: your own responsibilities might require you to send an @channel, but respect those of others as well. If you’re taking someone else’s time, be respectful of that time.

Good Uses

The FireAlarmEvent example we used earlier is a great example of this. A fire alarm going off changes how everyone in the building should behave: they should head for the exits. Specific people may have additional responsibilities, like grabbing their pets and children. So you have a responsibility yourself (the discovery), to them to let them know.

What about a real example? Well, in apps we usually don’t have fire alarms, but we do have to trigger and display errors. That fits the model: “I have a responsibility to some other component to tell them this happened. They may not know who I am, though, and I will not know who they are.”

We have minor emergencies that are probably appropriate for @channels, like an overflowing toilet. Some events are appropriate for @channels, too, like when the all-hands company meeting is starting. Some may use an @channel if they are locked out of the building. For each one of these, the rule applies: the recipient would want to know, and the sender is the responsible party to tell them, even though they do not know each other specifically.

In Conclusion: Hmm

If you take a hard look at usage of this rule, you will see that while the event or notification is a tool that you can use to do almost anything, its proper uses are far more rare. Good uses for events or notifications are difficult to find, because few events in an application have such a broad scope, or can be fired from so many places.

When I discussed notifications and events with my colleagues, the pain point that came up was what they called “nested” events. This is when one event ends up triggering another event, which triggers another event, and so on.

This might be the pain point, but it’s just not a problem that’s unique to events. All kinds of observers and listeners suffer from this same problem. If all you do is switch to using some other mechanism besides an event clearing house, you’ll still have the same problem.

That’s probably the biggest lesson I found here: there just aren’t any easy answers to this stuff. Unlike with the goto, getting rid of observers isn’t a realistic solution.

I like simple guiding rules for things. In practice, things are always going to get hairy. A simple rule is always going to be hard to apply, but it can be a guiding point on the horizon.

The post Event buses, NSNotificationCenter and @channels appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/event-buses-nsnotificationcenter-and-channels/feed/ 0
Announcing our Android Roadshow https://bignerdranch.com/blog/announcing-our-android-roadshow/ https://bignerdranch.com/blog/announcing-our-android-roadshow/#respond Sun, 27 Dec 2015 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/announcing-our-android-roadshow/ Our first Android roadshow is bringing one-day classes to various cities.

The post Announcing our Android Roadshow appeared first on Big Nerd Ranch.

]]>

I remember when I was first getting into professional programming in college, I thought it would be so cool that I would always have something new to learn, no matter how far along I’d get in this work. Yet here I am, 10 or so years into doing this, and that’s not how I always feel. I often wish that I could get on with the real work, and forget about learning the latest new thing.

Our Android Fundamentals class has always had that goal in mind: get on with the real work, regardless of what the latest new thing is. We keep course materials up-to-date, but they are meant to serve students well in the long run through teaching the fundamentals, not just the latest and greatest. Many developers have built whole new phases of their careers from what we teach in that class. Our new Advanced Android course goes into more depth, but it’s a similar idea: a deeper dive on ideas and tools that will be of long-term use.

Introducing the Big Nerd Ranch Android Roadshow

But sooner or later, you do need to focus on the shiniest new stuff. We’ve been in search of the right way to drill down on the shiny new stuff, so we’re rolling out something new this year that we hope you will dig. Here’s the idea:

  1. Two back-to-back one-day classes for experienced Android developers.
  2. We take it on the road, to cities across the United States.
  3. Brand new stuff every year.

The idea is to have focused annual professional development that you can fit into your budget and schedule. You’ll expand your existing Android knowledge in two days. Instead of diving down into underlying concepts, you get hands-on practice with strictly new stuff. And since we’ll have a variety of dates across the country, you’re more likely to find something close to you that’s running at a convenient time.

The classes themselves will be similar to our current courses, but looser and broader. Expect to have lots of hands-on time with one of our expert instructors, someone who helped write some of the material you’ll be working on. You can either work one of the independent study exercises we provide, or you can try to integrate new ideas into your own code in the provided workshop periods.

Is it like a conference? Sort of, yeah. Except with a lot more focus. Here are the two courses we’re writing:

Day one: Android Platform Features

The first course is all about the Android Platform. This includes new tools from the support libraries, but also includes new features in the standard libraries, and Android Marshmallow. Expect to learn more about:

  • CoordinatorLayout
  • Doze
  • The new permissions system
  • App linking
  • Direct share
  • Chrome custom tabs
  • Design support library
  • Annotations support library
  • GCM network manager

Get more info and sign up for our One-Day Android Platform Features class.

Day two: Community Tools

The second course focuses on community tools and libraries. This day has fewer topics, but a lot more work on each topic. On this day, you’ll cover:

  • Dagger 2
  • Retrofit 2
  • Kotlin
  • RxJava on Android
  • DataBinding

Get more info and sign up for our One-Day Android Community Tools class.

See you there!

We’re pretty excited about this. Our Android team has done some amazing work with these materials, and we know you’ll be blown away. We’re starting with Atlanta and San Francisco for now, but we’ll expand to more cities in the future. Let us know in the comments where you’d like to see us!

Oh yeah, one last thing: signing up! Check out our class schedule for info. Classes are just $700 each. Sign up for both classes, and you’ll get $200 off. Which is exactly the same as us giving you $200 for free. (Not really. But it’s still pretty nice.)

The post Announcing our Android Roadshow appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/announcing-our-android-roadshow/feed/ 0
Android Programming: The Big Nerd Ranch Guide, Second Edition https://bignerdranch.com/blog/android-programming-the-big-nerd-ranch-guide-second-edition/ https://bignerdranch.com/blog/android-programming-the-big-nerd-ranch-guide-second-edition/#respond Wed, 22 Jul 2015 09:53:37 +0000 https://nerdranchighq.wpengine.com/blog/android-programming-the-big-nerd-ranch-guide-second-edition/ It's over! Brian, Kristin, Chris and I are finally done working on the second edition of [Android Programming: The Big Nerd Ranch Guide, Second Edition](http://www.amazon.com/Android-Programming-Ranch-Guide-Edition/dp/0134171454/ref=dp_ob_title_bk).

Looking back on it, though, it's all a blur. We spent months working on it! We must have been up to *something*.

The post Android Programming: The Big Nerd Ranch Guide, Second Edition appeared first on Big Nerd Ranch.

]]>

It’s over! Brian, Kristin, Chris and I are finally done working on the second edition of Android Programming: The Big Nerd Ranch Guide, Second Edition.

Looking back on it, though, it’s all a blur. We spent months working on it! We must have been up to something.

A New IDE

Oh yeah—how could I have forgotten about the new IDE? Google released Android Studio last year. It was possible to work through the old book in Android Studio, but it was getting more and more frustrating with each passing month.

The new edition doesn’t have that problem. The entire thing is done using Android Studio: all the shortcuts and screenshots have been updated, making the reader’s experience much more pleasant.

Android Studio is a delightful development environment, and we’ve put some effort into showing off how it can make your life easier as a developer.

New Versions Of Android

Of course, that’s not all that’s changed. Android has seen a few new releases since we published the first edition of our guide. This might be the most important thing for some people who are thinking about reading our book. They want to see it on the cover: “This book covers the latest and greatest: Android 5.1!”

I remember writing a whole chapter on implementing material design, as well as sections describing how JobScheduler works. So I’m pretty sure readers will find a lot of Android 5.1 stuff in the book. (We’ve even managed to squeeze in a few bits about the upcoming M release.)

A Changing World Of Devices

For someone scanning books on Amazon, the number “5.1” is their best clue that a book is up-to-date. Android 5.1 is not yet installed on most devices, though. We’d be doing our readers a disservice if all we did was add a bunch of new content for a version of Android you can’t yet focus on.

In our first edition, many devices were still running Android 2.3 Gingerbread, so our code samples all ran on that version. Our new code listings are a lot cleaner now: they get rid of that ugly compatibility code, focusing on Android Jelly Bean and later releases. So we must have gone through and fixed all that stuff.

New Compatibility Tools

What about all the stuff in the support libraries that has changed? People don’t usually look for that on the cover, but that’s some of the coolest stuff in Android! If I were reading a book, I’d want to know about cool stuff like the RecyclerView and the new AppCompatActivity. And what about FloatingActionButton, or Snackbar? Those things are so handy, I would feel really bad if we published a new version without talking about them somewhere.

Wait—hmm. I guess we put those in the new edition, too.

New and Updated Chapters

Jeez, that’s a lot of new stuff. We really put a lot of work into this thing.

But wait a second; there are some new chapters in this table of contents, too. There’s one here on property animators that I don’t think was in the first edition, plus a whole chapter on material design implementation tools. Who wrote a whole chapter on how theming works? That’s pretty awesome (I always hated having to deal with that stuff).

The older chapters look like they’ve been updated, too. SQLite is much earlier in the book than it was in the first edition—it looks like the big CriminalIntent example has a database back end now, which is pretty nifty. And the entire mapping and location exercise sequence has been completely rewritten, with a brand new sample app.

Go get it!

I’m playing it up a bit for this blog post, but I’m not kidding, either. I had really forgotten how much stuff we put into this edition. I didn’t even talk about all the work we put into refining and polishing, fixing continuity issues and bugs, either.

The whole point, of course, is to make your time getting up to speed on the latest and greatest in Android development easy and pleasant. So go pre-order a copy! We hope you find it useful. (We also hope that you remember the stuff we put in there better than I did.)

The post Android Programming: The Big Nerd Ranch Guide, Second Edition appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/android-programming-the-big-nerd-ranch-guide-second-edition/feed/ 0
RecyclerView Part 2: Choice Modes https://bignerdranch.com/blog/recyclerview-part-2-choice-modes/ https://bignerdranch.com/blog/recyclerview-part-2-choice-modes/#respond Thu, 18 Dec 2014 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/recyclerview-part-2-choice-modes/ Let's take another look at choice modes in Android Lollipop's RecyclerView.

The post RecyclerView Part 2: Choice Modes appeared first on Big Nerd Ranch.

]]>

Don’t miss Part 1, where Bill addresses the fundamentals of RecyclerView for ListView experts.

A famous man once said,

In this life, things are much harder than in the afterworld. In this life, you’re on your own.

Is this a true statement? Perhaps that matter is up for debate. When it comes to selecting items in a RecyclerView, though, you are, in fact, on your own: RecyclerView does not give you any tools for doing this. So how do you do it?

I figured this would be a straightforward exercise in rolling my own solution, so I dove right in. Here’s what I found out.

(If you like, you can see how I worked through all this in my GitHub repo. And if you just want to know how to do it the easy way, skip to the section named “TL;DR” at the end.)

Review: Choice Modes And Contextual Action Modes

I set out to implement multiselect like we do in the CriminalIntent app in our Android book: with a contextual action mode. Here’s what that looks like in the code (for brevity, I’m showing only the interesting bits—you can find the whole thing in our solutions):

    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
    
        public boolean onCreateActionMode(ActionMode mode, Menu menu) { ...  }
    
        public void onItemCheckedStateChanged(ActionMode mode, int position,
                long id, boolean checked) { ...  }
    
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_item_delete_crime:
                    CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
                    CrimeLab crimeLab = CrimeLab.get(getActivity());
                    for (int i = adapter.getCount() - 1; i >= 0; i--) {
                        if (getListView().isItemChecked(i)) {
                            crimeLab.deleteCrime(adapter.getItem(i));
                        }
                    }
                    mode.finish(); 
                    adapter.notifyDataSetChanged();
                    return true;
                default:
                    return false;
            }
  
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) { ...  }
        
        public void onDestroyActionMode(ActionMode mode) { ...  }
    });

ListView has this idea of choice modes. If the ListView is in a particular choice mode, it will handle all the details of displaying a checkable interface, keeping track of check marks and toggling all that stuff back and forth when individual items are tapped. You turn choice modes on by calling ListView.setChoiceMode(), as you see above. To see whether an item is checked, you call ListView.isItemChecked(int) (like you can see above in onActionItemClicked()).

When you use CHOICE_MODE_MULTIPLE_MODAL, long pressing any item in the list will automagically turn multichoice mode on. At the same time, it will activate an Action mode representing the multiselect interaction. The MultiChoiceModeListener above is a listener for that contextual action mode—it’s like a set of option mode callbacks that are only called for this action mode.

In my earlier post on RecyclerView fundamentals, we saw that RecyclerView leaves you on your own for implementing all of this. You have three moving parts that need to be implemented:

  • showing which views are checked and unchecked
  • keeping track of checked and unchecked states for items in the list
  • turning the whole thing off and on in a contextual action mode

In a perfect world, this will be something that you would actually want to do in practice, too. As I was writing this, though, I found my solutions falling short. I could imagine someone reading this post and just shaking their head: “Seriously? I need to roll this myself every time?”

So in this post, I’ll explain enough that you can roll your own easily if you need to, but also provide a library called MultiSelector that’s more of a drop-in solution.

Keeping Tracked Of State

This is the most straightforward, so let’s solve it first. In ListView, this works like so:

    // Check item 0
    mListView.setItemChecked(0, true);

    // Returns true
    mListView.isItemChecked(0);

    // Says what the choice mode currently is
    mListView.getChoiceMode();

Rolling our own looks like this:

    private SparseBooleanArray mSelectedPositions = new SparseBooleanArray();
    private mIsSelectable = false;

    private void setItemChecked(int position, boolean isChecked) {
        mSelectedPositions.put(position, isChecked);
    }

    private boolean isItemChecked(int position) {
        return mSelectedPositions.get(position);
    }

    private void setSelectable(boolean selectable) {
        mIsSelectable = selectable;
    }

    private boolean isSelectable() {
        return mIsSelectable;
    }

This will not update the user interface like ListView.setItemChecked(), but it will do for now.

Of course, you can keep track of selections however you like. A Set of model objects can be a good choice, too.

I put this idea in an object called MultiSelector:

    MultiSelector selector = new MultiSelector();
    selector.setSelected(0, true);
    selector.isSelected(0);
    selector.setSelectable(true);
    selector.isSelectable();

Showing Item Selection State

In ListView from Honeycomb onwards, item selection has been visualized like this: whenever an item is selected, the view is set to the “activated” state by calling setActivated(true). When the view is no longer selected, it is set back to false. With that done, it is straightforward to tweak the selection mode by using XML StateListDrawables to highlight the selection state.

You can do the same thing by hand in your ViewHolder’s bindCrime:

    private class CrimeHolder extends ViewHolder {
        ...

        public void bindCrime(Crime crime) {
            mCrime = crime;
            mSolvedCheckBox.setChecked(crime.isSolved());

            boolean isSelected = mMultiSelector.isSelected(getPosition());
            itemView.setActivated(isSelected);
        }
    }

Of course, if you want to display selection in another way, you can. The sky’s the limit. Drawables and state list animators make the activated state a good default choice, though.

If that were all there was, I wouldn’t have spent so much time on this. But I did, because I got stubborn on some visual details I wanted.

Material animations

Material Design includes this cool new ripple animation. If you read more about it at Implementing Material Design in Your Android App, you see that you can get this behavior for free when you use ?android:selectableItemBackground as your background.

If you are going to use the activated state, though, this is not an option. ?android:selectableItemBackground does not support visualization for the activated state. You can try to roll your own with activated support using a state selector drawable, but that ends up looking like this:

Ripples in a state selector

The selected state responds each time you tap on it. So when you tap the view to turn activated state off, you also get the ripple effect.

This did not make sense to me visually. In my mind, the list has two modes: normal mode, and selection mode. In normal mode, a tap should have the same ripple effect that ?android:selectableItemBackground gives me. In selection mode, though, a tap should simply toggle activated on and off, without any ripple effect. In Lollipop, it would be nice to also have a Material Design affordance: a state list animator to elevate the selected items up in translation.

To get this effect with the out of the box Android APIs, you have to do more than use state list drawables and animators judiciously. You need to actually have two different modes for the view: one in which it uses a default set of drawables & animators, and one in which it uses a different set exclusively for selection. Like this:

Two mode selection view

SwappingHolder

This is where the second tool I wrote comes into play: a ViewHolder subclass called SwappingHolder, which does exactly what I just described. SwappingHolder subclasses the regular ViewHolder and adds six additional properties:

    public Drawable getSelectionModeBackgroundDrawable();
    public Drawable getDefaultModeBackgroundDrawable();

    public StateListAnimator getSelectionModeStateListAnimator();
    public StateListAnimator getDefaultModeStateListAnimator();

    public boolean isSelectable();
    public boolean isActivated();

When you first create it, SwappingHolder will leave its itemView’s background drawable and state list animator alone, stashing those initial values in defaultModeBackgroundDrawable and defaultModeStateListAnimator. If you set selectable to “true,” though, it will switch to the selectionMode version of both those properties. Set selectable back to “false,”” it switches back to the default value. And the activated property? It calls through to itemView’s activated property.

Out of the box, SwappingHolder uses a selectionModeStateListAnimator that elevates the selected item up a little bit when activated, and a selectionModeBackgroundDrawable that uses the colorAccent attribute out of the appcompat Material theme.

So that fixes that. The last bit is to hook everything up to the selection logic in a way that’s easy to turn on and off.

Connecting The Selection Logic

Again, you can do it by hand if you like. There are two steps: updating the ViewHolder when it is bound to a crime, and hooking up click events. To update when bound to a crime, add some more code to bindCrime():

    private class CrimeHolder extends SwappingHolder {
        ...

        public void bindCrime(Crime crime) {
            mCrime = crime;
            mSolvedCheckBox.setChecked(crime.isSolved());

            setSelectable(mMultiSelector.isSelectable());
            setActivated(mMultiSelector.isSelected(getPosition()));
        }
    }

So every time you hook up your ViewHolder to another crime, you need to double check and see whether you’re currently in the selection mode, and whether the item you’re hooking up to is selected.

And then hook up a click listener:

    private class CrimeHolder extends SwappingHolder 
            implements View.OnClickListener {
        ...

        public CrimeHolder(View itemView) {
            super(itemView);

            mSolvedCheckBox = (CheckBox) itemView
                .findViewById(R.id.crime_list_item_solvedCheckBox);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mMultiSelector.isSelectable()) {
                // Selection is active; toggle activation
                setActivated(!isActivated());
                mMultiSelector.setSelected(getPosition(), isActivated());
            } else {
                // Selection not active
            }
        }
    }

For single select, the onClick() implementation will need to be more complicated than that, because it will need to find the other currently active selection and deselect it.

This isn’t a whole lot of code, but it is boilerplate that you would need to implement every time you write this. I’ve done some more work in MultiSelector that gets rid of the boilerplate.

Turning Everything On And Off

Okay, the last step: turning it on and off. You definitely need to do this for CHOICE_MODE_MULTIPLE_MODAL, and you often need to when using the other choice modes, too.

The simplest solution is to augment your setSelectable() implementation with a notifyDataSetChanged():

    public void setSelectable(boolean isSelectable) {
        mIsSelectable = isSelectable;
        mRecyclerView.getAdapter().notifyDataSetChanged();
    }

In ListView (and in ViewPager), notifyDataSetChanged() was almost always the right solution when you were showing the wrong thing. In RecyclerView, I recommend that you be much more judicious about using it.

Here’s why: the biggest reason to use RecyclerView is that it makes it easy to animate changes to your list content. For example, if you want to delete the first crime from your list, you can animate that like this:

    // Delete the 0th crime from your model
    mCrimes.remove(0);
    // Notify the adapter that it was removed
    mRecyclerView.getAdapter().notifyItemRemoved(0);

Calling notifyDataSetChanged() can break that, because it interrupts those animations.

The RecyclerView’s ItemAnimator will animate the change for you. The default animator will fade out item 0, then shift the other items up one.

What happens if you do notifyDataSetChanged() soon after that? It will kill any pending animations, requery the adaptor and redisplay everything. A heavy hammer, that. Often it’s the right choice anyway, but be aware: if you can update your list content in some other way besides notifyDataSetChanged, do it!

So what other way could we do this? Well… like this:

    public void setSelectable(boolean isSelectable) {
        mIsSelectable = isSelectable;
        for (int i = 0; i < mRecyclerView.getAdapter().getItemCount(); i++) {
            RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForPosition(i);

            if (holder != null) {
                ((SwappingHolder)holder).setSelectable(isSelectable);
            }
        }

    }

We can iterate over all the ViewHolders, cast them to SwappingHolder and manually tell them what the current selectable state is. Yech.

Like with SwappingHolder, MultiSelector takes care of this for you. MultiSelector knows which ViewHolders are hooked up, so this line is all you need to update your user interface:

    mMultiSelector.setSelectable(true);

Using A Contextual Action Mode

Once setSelectable() is implemented, you can achieve the rest of CHOICE_MODE_MULTIPLE_MODAL by using a regular ActionMode.Callback. Call through to your setSelectable() from within the relevant callback methods:

    private ActionMode.Callback mDeleteMode = new ActionMode.Callback() {
        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
            setSelectable(true);
            return false;
        }

        @Override
        public void onDestroyActionMode(ActionMode actionMode) {
            setSelectable(false);
        }

        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { ... }

        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { ... }
    }

Then use a long click listener to turn on the action mode:

    private class CrimeHolder extends SwappingHolder
            implements View.OnClickListener, View.OnLongClickListener {

        ...

        public CrimeHolder(View itemView) {
            ...

            itemView.setOnClickListener(this);
            itemView.setOnLongClickListener(this);
            itemView.setLongClickable(true);
        }

        @Override
        public boolean onLongClick(View v) {
            ActionBarActivity activity = (ActionBarActivity)getActivity();
            activity.startSupportActionMode(deleteMode);
            setSelected(this, true);
            return true;
        }
    }

TL;DR: Implementing Choice Modes with a Library

Ok, so that’s everything going on in MultiSelect. What if you don’t care, and would prefer to have an out-of-the-box solution?

One existing solution has been brought to my attention: TwoWayView, a library by Lucas Rocha. I haven’t had time to research the details of how it does what it does, but I can tell you that it sets out to replicate the setChoiceMode() API used by ListView, as well as a lot of other stuff that ListView had. For folks looking to drop-in replace their old ListView with a RecyclerView-based implementation, TwoWayView looks like a great solution. If you’d like to use that, I defer to their documentation.

Of course, by the time my colleagues told me about this, I had already written my own multiselect implementation that looked much different. Maybe you will find it useful, too. I’ve tried to make something small, focused, flexible and easy to use. There’s not a lot of code, and only a limited amount of judiciously chosen “magic.” Here’s how it works.

MultiSelector: Basics

First, import the library. Add the following line to your build.gradle:

compile 'com.bignerdranch.android:recyclerview-multiselect:+'

(You can find the project on GitHub, along with the Javadocs.)

Next, create a MultiSelector instance. In my example app, I did it inside my Fragment:

    public class CrimeListFragment extends Fragment {

        private MultiSelector mMultiSelector = new MultiSelector();

        ...
    }

The MultiSelector knows which items are selected, and is also your interface for controlling item selection across everything it is hooked up to. In this case, that’s everything in the adapter.

To hook up a SwappingHolder to a MultiSelector, pass in the MultiSelector in the constructor, and use click listeners to call through to MultiSelector.tapSelection():

    private class CrimeHolder extends SwappingHolder
            implements View.OnClickListener, View.OnLongClickListener {
        private final CheckBox mSolvedCheckBox;
        private Crime mCrime;

        public CrimeHolder(View itemView) {
            super(itemView, mMultiSelector);

            mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.crime_list_item_solvedCheckBox);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if (mCrime == null) {
                return;
            }
            if (!mMultiSelector.tapSelection(this)) {
                // start an instance of CrimePagerActivity
                Intent i = new Intent(getActivity(), CrimePagerActivity.class);
                i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
                startActivity(i);
            }
        }
    }

MultiSelector.tapSelection() simulates tapping a selected item; if the MultiSelector is in selection mode, it returns true and toggles the selection for that item. If not, it returns false and does nothing.

To turn on multiselect mode, call setSelectable(true):

    mMultiSelector.setSelectable(true);

This will toggle the flag on the MultiSelector, and toggle it on all its bound SwappingHolders, too. This is all done for you by SwappingHolder—it extends MultiSelectorBindingHolder, which binds itself to your MultiSelector.

And for basic multiselect, that’s all there is to it. When you need to know whether an item is selected, ask the multiselector:

    for (int i = mCrimes.size(); i > 0; i--) {
        if (mMultiSelector.isSelected(i, 0)) {
            Crime crime = mCrimes.get(i);
            CrimeLab.get(getActivity()).deleteCrime(crime);
            mRecyclerView.getAdapter().notifyItemRemoved(i);
        }
    }

Single selection

To use single selection instead of multiselect, use SingleSelector instead of MultiSelector:

    public class CrimeListFragment extends Fragment {

        private MultiSelector mMultiSelector = new SingleSelector();

        ...
    }

To get the same effect as CHOICE_MODE_MULTIPLE_MODAL, you can either write your own ActionMode.Callback as described above, or use the provided abstract implementation, ModalMultiSelectorCallback:

    private ActionMode.Callback mDeleteMode = new ModalMultiSelectorCallback(mMultiSelector) {

        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context, menu);
            return true;
        }

        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
            switch (menuItem.getItemId()) {
                case R.id.menu_item_delete_crime:
                    // Delete crimes from model

                    mMultiSelector.clearSelections();
                    return true;
                default:
                    break;
            }
            return false;
        }
    };

ModalMultiSelectorCallback will call MultiSelector.setSelectable(true) and clearSelections() inside onPrepareActionMode, and setSelectable(false) in onDestroyActionMode. Kick it off like any other action mode inside a long click listener:

    private class CrimeHolder extends SwappingHolder
            implements View.OnClickListener, View.OnLongClickListener {
        public CrimeHolder(View itemView) {
            ...

            itemView.setOnLongClickListener(this);
            itemView.setLongClickable(true);
        }

        @Override
        public boolean onLongClick(View v) {
            ActionBarActivity activity = (ActionBarActivity)getActivity();
            activity.startSupportActionMode(mDeleteMode);
            mMultiSelector.setSelected(this, true);
            return true;
        }
    }

Customizing selection visuals

SwappingDrawable uses two sets of drawables and state list animators for its itemView: one while in the default mode, and one while in selection mode. You can customize these by calling one of the various setters:

    public void setSelectionModeBackgroundDrawable(Drawable drawable);
    public void setDefaultModeBackgroundDrawable(Drawable drawable);

    public void setSelectionModeStateListAnimator(int resId);
    public void setDefaultModeStateListAnimator(int resId);

The state list animator setters are safe to call prior to API 21, and will result in a no-op.

Off label customization

If you need to customize what the selected states look like beyond what SwappingHolder offers, you can extend the MultiSelectorBindingHolder abstract class:

    public class MyCustomHolder extends MultiSelectorBindingHolder {
        @Override
        public void setSelectable(boolean selectable) { ... }

        @Override
        public boolean isSelectable() { ... }

        @Override
        public void setActivated(boolean activated) { ... }

        @Override
        public boolean isActivated() { ... }
    }

And if that’s still too restrictive, you can implement the SelectableHolder interface instead, which provides the same methods. It requires a bit more code: you will need to bind your ViewHolder to the MultiSelector by calling mMultiSelector.bindHolder() every time onBindViewHolder is called.

Had Enough?

In this post, we took a look at selecting items in a RecyclerView. From working along, you now know how to show which views are checked and unchecked, track checked and unchecked states for items in the list, and turn the whole thing off and on in a contextual action mode.

The post RecyclerView Part 2: Choice Modes appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/recyclerview-part-2-choice-modes/feed/ 0
RecyclerView Part 1: Fundamentals For ListView Experts https://bignerdranch.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/ https://bignerdranch.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/#respond Thu, 04 Dec 2014 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/ Android Lollipop's RecyclerView makes animating items into and out of a list of content much easier. Getting RecyclerView to cleanly transition into a multiple choice caused some interesting problems, however. Here's how to deal with them.

The post RecyclerView Part 1: Fundamentals For ListView Experts appeared first on Big Nerd Ranch.

]]>

Time to come clean: I was late to the game on RecyclerView. Way late!

I have only myself to blame. You can tell from its description that RecyclerView is supposed to replace ListView, and there aren’t a lot of Views more important than ListView in the toolbag. Seems pretty clear: RecyclerView is important.

But it’s also a lot different, right? Ugh. So I put it off until a couple of weeks ago, when I was putting together a talk for one of our Hack Nights. I ended up doing a fair amount of research, which was—I admit it—a lot of fun.

It turns out that RecyclerView is really cool, and worth switching to. It makes a lot of previously hard things much, much, much easier. Most importantly, it makes animating items into and out of a list of content doable in an hour or two, instead of a couple of days.

To figure out all this stuff, I decided to swap out the ListView in our Android programming guide’s CriminalIntent exercise to use RecyclerView. I found that most of the work is easy, and the final code satisfyingly clean.

Except for one bit: choice modes. The setChoiceMode() method is gone, and getting a really solid RecyclerView that cleanly transitions into a multiple choice caused some interesting problems. Over the course of a few blog posts, I’ll talk about the path I followed, and share my solutions with you. I’ll start here by talking about the basics of RecyclerView.

RecyclerView Does Less

Let’s talk a bit about the big changes in RecyclerView if you’re trying to replace a ListView. Step zero, of course, is to import the RecyclerView library with this line in your build.gradle:

compile 'com.android.support:recyclerview-v7:+'

Next: there is a reason setChoiceMode(int) is gone, and it’s a good one. RecyclerView can do more than ListView, but the RecyclerView class itself has fewer responsibilities than ListView. Out of the box, RecyclerView does not:

  • position items on the screen
  • animate views
  • handle any touch events apart from scrolling

All of this stuff was baked in to ListView, but RecyclerView uses collaborator classes to do these jobs instead.

For the first two, RecyclerView uses LayoutManager and ItemAnimator. RecyclerView comes with a default ItemAdapter, so you need not worry about that. You do have to give it a LayoutManager to position the items. Here is what our CriminalIntent app’s list fragment onCreateView() looks like with a RecyclerView:

        @Override
    public View onCreateView(LayoutInflater inflater, 
            ViewGroup parent, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_recyclerview, parent, false);

        mRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        mCrimes = CrimeLab.get(getActivity()).getCrimes();
        mRecyclerView.setAdapter(new CrimeAdapter());

        return v;
    }

LinearLayoutManager lives in the RecyclerView support library. It makes your RecyclerView position its items exactly like ListView did. There are other layout managers to layout in a grid, or a staggered grid. As for CrimeAdapter, I’ll talk about that in a moment.

To tell when an item is clicked on ListView, you would do something like this:

        public View onCreateView(LayoutInflater inflater, ViewGroup parent,
            Bundle savedInstanceState) {
        ...

        mListView.setOnItemClickListener(this);

        ...
    }

    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        // handle item click
    }

You could also handle click events through listeners on the individual items your adapter returns. This was not common or recommended, though, because ListView gave you some nice things like ListView.setChoiceMode(int), which built selectable items on top of click handling.

However, RecyclerView has mostly shed responsibility for those events, which is why it does not have choice modes. You cannot provide an OnItemClickListener to tell when items are selected. RecyclerView does provide an OnItemTouchListener, but this isn’t the same thing: it does not tell you which item was touched. You can use the MotionEvent to find out which item was touched, but for most situations this is more work than is necessary.

ViewHolder

The other big difference important to know here is the elevated role of the view holder. If you learned how to write ListViews from us, you may not know about the view holder pattern because we don’t teach it. A view holder is an object attached to each row in your ListView. A typical implementation would look like this:

        private static class ViewHolder {
        private CheckBox mSolvedCheckBox;
    }

    private class CrimeAdapter extends ArrayAdapter<Crime> {
        public CrimeAdapter(ArrayList<Crime> crimes) {
            super(getActivity(), 0, crimes);
        }

        @Override
        public View getView(int position, View convertView, 
                ViewGroup parent) {
            ViewHolder holder;
            if (null == convertView) {
                convertView = LayoutInflater.from(getActivity())
                    .inflate(R.layout.list_item_crime, parent, false);
                holder = new ViewHolder();
                holder.mSolvedCheckBox = (CheckBox) convertView
                    .findViewById(R.id.solvedCheckBox);

                convertView.setTag(holder);
            }

            ViewHolder holder = (ViewHolder) convertView.getTag();
            Crime crime = getItem(postion);
            holder.mSolvedCheckBox.setChecked(crime.isSolved());

            return convertView;
        }
    }

When the view is inflated, you also create an instance of this ViewHolder object and populate it with data you are interested in. Then you attach it to the view by calling setTag().

For each view you create, you create a ViewHolder, too. ViewHolder has historically been used to achieve better scrolling performance: it saves the need to call findViewById() every time you need to access solvedCheckBox. If you have other costly operations that you would rather not perform on each call to getView(), you can stash results from those in the ViewHolder as well.

Here is how we would prefer to write the equivalent of the above in a RecyclerView:

        private class CrimeHolder extends ViewHolder {
        private final CheckBox mSolvedCheckBox;
        private Crime mCrime;

        public CrimeHolder(View itemView) {
            super(itemView);

            mSolvedCheckBox = (CheckBox) itemView
                .findViewById(R.id.crime_list_item_solvedCheckBox);
        }

        public void bindCrime(Crime crime) {
            mCrime = crime;
            mSolvedCheckBox.setChecked(crime.isSolved());
        }
    }

    private class CrimeAdapter 
            extends RecyclerView.Adapter<CrimeHolder> {
        @Override
        public CrimeHolder onCreateViewHolder(ViewGroup parent, int pos) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item_crime, parent, false);
            return new CrimeHolder(view);
        }

        @Override
        public void onBindViewHolder(CrimeHolder holder, int pos) {
            Crime crime = mCrimes.get(pos);
            holder.bindCrime(crime);
        }

        @Override
        public int getItemCount() {
            return mCrimes.size();
        }
    }

Note that there is no ArrayAdapter, so your Adapter must hook up to a List by hand. This isn’t too hard, thankfully.

Just like in the ListView Adapter example, you have two classes here: an Adapter and a ViewHolder. In a RecyclerView adapter, though, the ViewHolder is more of a keystone piece of the system. Instead of creating and binding Views, your RecyclerView.Adapter creates and binds ViewHolders.

The ViewHolders you create are beefier, too. They subclass RecyclerView.ViewHolder, which has a bunch of methods RecyclerView uses. ViewHolders know which position they are currently bound to, as well as which item ids (if you have those).

In the process, ViewHolder has been knighted. It used to be ListView’s job to hold on to the whole item view, and ViewHolder only held on to little pieces of it. Now, ViewHolder holds on to all of it in the ViewHolder.itemView field, which is assigned in ViewHolder’s constructor for you.

Since ViewHolder has this new responsibility, it starts to make a lot of sense to give it other responsibilities, too. So in the new implementation we’ve added a new method, bindCrime(), which does a lot of the plumbing work that used to go into getView(). ViewHolder also becomes the most natural place to handle any click events for your specific item:

        private class CrimeHolder extends ViewHolder 
            implements View.OnClickListener {
        ...

        public CrimeHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);

            ...
        }

        @Override
        public void onClick(View v) {
            if (mCrime != null) {
                Intent i = CrimeActivity.getIntent(v.getContext(), mCrime);
                startActivity(i);
            }
        }
    }

In ListView, there was some ambiguity about how to handle click events: Should the individual views handle those events, or should the ListView handle them through OnItemClickListener? In RecyclerView, though, the ViewHolder is in a clear position to act as a row-level controller object that handles those kinds of details.

We saw earlier that LayoutManager handled positioning views, and ItemAnimator handled animating them. ViewHolder is the last piece: it’s responsible for handling any events that occur on a specific item that RecyclerView displays.

Choice Modes and What’s Next

So what about choice modes? Where do they fit into all this? In the way we’ve shown here, into the ViewHolder, naturally enough, because it handles item click events.

This is where I have some bad news: ViewHolder does not give you any tools out of the box to implement selection. To get single or multiselect going, I’ve got two problems that I will address in my next blog post:

  1. Keeping track of selections. There needs to be some code that knows which items are selected, which are not, and whether selection is activated or not.

  2. Showing which items are selected. ListView handled this for you by calling setActivated() on each item’s view. You can do that on Lollipop, too, but there are a few gotchas in implementing it smoothly.

Learn how to tackle choice modes in Lollipop in Part 2 of this series.

The post RecyclerView Part 1: Fundamentals For ListView Experts appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/feed/ 0
Real Talk About Lollipops https://bignerdranch.com/blog/real-talk-about-lollipops/ https://bignerdranch.com/blog/real-talk-about-lollipops/#respond Mon, 20 Oct 2014 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/real-talk-about-lollipops/ Android 5.0 Lollipop arrived last Thursday, with much hoopla. Unquestionably, Lollipop is the most significant Android release since Android 3.0 Honeycomb over three and a half years ago. Here's what we're looking forward to.

The post Real Talk About Lollipops appeared first on Big Nerd Ranch.

]]>

It’s that time of the year: a new version of Android has graced us once again. Android 5.0 Lollipop arrived last Thursday, with much hoopla. Unquestionably, Lollipop is the most significant Android release since Android 3.0 Honeycomb more than three and a half years ago.

Big New Changes

Honeycomb turned Android design upside down with Holo and a new set of design docs, and it turned developer implementations on their head with powerful interface architecture tools.

So what’s the story with Lollipop? Part of it is similar: there is a major redesign again, this time called Material, that includes changes to how users navigate within apps.

The other part looks different, though. Instead of a few major concepts that need to be reckoned with, Lollipop has a raft of changes and important APIs, plus one or two sweeping changes under the hood.

Developer Priorities Right Now

Let’s talk about the most important stuff first. What do you need to change right now?

In Android, the targetSdkVersion value in your AndroidManifest usually lets the device adapt to your older applications, rather than the other way around. Lollipop is different, though, because it introduces a new runtime called Android Runtime (ART).

For most apps, ART should work fine. But if you have an app that relies on advanced techniques like adjusting the stack size, JNI or reflection on the Object class, you may need to scramble to be able to run on new Lollipop phones. For the technical details, check out the guide to ART compatibility issues on Google’s Android site. And earlier this year, Kurt Nelson wrote a post about replacing Dalvik with the Android Runtime that you should check out.

Updating the Look of Your App

We strongly recommend that you update the look and feel of your existing apps to fit in with the new Material design. Designers will want to consult the new Material Design documentation for reference, in addition to a forthcoming blog post here.

For programmers, the basic changes and tools are easy, and can be applied across all versions of Android, not just Lollipop. Some of the new graphical sheen will be active only on Lollipop, but your app’s code will be the same as on earlier versions.

The Material theme is available right now: just include version 21 of the appcompat library in your app. Switch your app’s theme to Theme.AppCompat, and you’re good to go.

Material also pushes apps towards more dynamic, animated user interfaces. Developers familiar with the existing tools cannot be blamed if they groan at the prospect of adding the exciting new list and grid animations in their existing ListView code, though. The latest appcompat library introduces the RecyclerView, however, which makes animating items into and out of lists simple. The API is much different from the old ListView, so expect to spend a minute or two familiarizing yourself with it.

New Features, New Apps

Those with apps in some niches should also consider checking out new APIs that are available:

  • If your app uses the old Camera Java API, look to the new Camera2 API, which is worlds better. It is now possible to configure and program the camera as a pipelined device, not just as a Java object that you call synchronous methods on. This is a big change, but means new possibilities: burst mode, improved video capture and the ability to write richer apps that use the camera in a variety of ways simultaneously. Expect to see some cool new capabilities in Android camera and video apps.

  • More improvements to audio latency. Because Android has lacked low-latency audio input, apps like iRig (which lets you plug your guitar into your phone and rock out) have suffered from input lag, which makes them unusable for real musicians. In Lollipop, those apps should see improvements.

  • You can also plug in USB audio devices—something iOS can’t do.

  • You can now render PDFs on Android out of the box. Many apps were doing this already, but had to rely on licensed libraries to do so.

The Lollipop Market

Historically, it has taken about two years for new Android versions to reach 75 percent of the market. This sounds rough, but most apps are not impacted in practice. That said, if you want to target the new camera API, audio latency, USB audio or PDF rendering features, you will need to either make those features available only on newer devices or limit your market to those devices.

Lollipop is another important step up for Android, and a major release in every way. We continue to be excited to watch where Google is taking Android for app developers and Android users. And of course, stay tuned for updates to our Android bootcamp!

The post Real Talk About Lollipops appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/real-talk-about-lollipops/feed/ 0