Building a Grid Layout With RecyclerView and Realm
Building an app with RecyclerView? Realm Java is an easy way to power a list or grid, and supports any type of data you can design. See how simple it is!
Nicolas worked over the years on various projects at Google, from the HTML5 support in Android Browser, implementing the webview hardware acceleration in Honeycomb, or writing a scalable and non-destructive photo editor in JB/KitKat. After a year building high-speed telemetry systems for robots, he came back to Android, developed ConstraintLayout and now works on the Android Studio layout editor.
I work at Google, and during my tenure, I’ve been working on a framework for Android.
For the past year and a half, I’ve been working on ConstraintLayout. I work on the tools, but have some experience as a developer. My goal with ConstraintLayout was to create something that helps developers.
Why ConstraintLayout? (0:45)
If you remember at Google I/O last May, we announced a fancy editor, Layout Editor. The reason why we did ConstraintLayout is tie it to the editor and vice versa.
I wanted to do a new layout Editor. Something that would be really powerful and easy to use. In order to do that, I felt I needed a ViewGroup that was more powerful and flexible - something that would work better with an ID. The result is ConstraintLayout and it’s very flexible. It allows you to easily create a flat hierarchy, which is a good thing.
ConstraintLayout is also an unbundled library and that’s important both for you as developers and for us as the creator of the library. It’s good for you because you’re in full control. When we read something, you get it immediately and you decide. It’s not pushed on to you. You can do a test and once you have a version of your optiware, you ship with that. If we ship another version the day after, you are not going to have everything that goes wrong.
It’s good for us because of this exact reason. We can actually push a new version. We don’t have to worry about breaking stuff. That also means we could sometimes clean up the API or add new features. Finally, because it’s unbundled, it’s also available for any device at this point.
RelativeLayout is also a Layout that you might have used in the past, and on the surface it has a lot of similarities with ConstraintLayout. You can position your children relative to each other, which at first glance is exactly what ConstraintLayout does. There are a couple of issues with RelativeLayout that you may be aware of.
This is a completely contrived example, but I have this button that’s constraint to the left and bottom of the container.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http:// schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_marginBottom="34dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" android:text="Button" /> </RelativeLayout>
What happens when you set the container to Wrap Content on the height?
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http:// schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_marginBottom="34dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" android:text="Button" /> </RelativeLayout>
The button stays at the bottom and it does that simply because the way RelativeLayout works. It’s not personally what I was expecting.
If we do the exact same thing in ConstraintLayout, when you call Wrap Content, it wraps.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/button12" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="34dp" android:layout_marginEnd="24dp" app:layout_constraintRight_toRightOf="parent" android:layout_marginRight="24dp" /> </android.support.constraint.ConstraintLayout>
You can also look at the XML and you can see that it’s not that horrible compared to RelativeLayout. In fact, the only two attributes that you do have that are new are those two. They’re here to enforce a constraint on the bottom and on the right to the parent container. All other attributes in the XML are attributes you already know, starting out with the height, the margin, et cetera.
Nested Layout (4:34)
You can do a flat hierarchy where you can imagine a layout that is not complex. You have a horizontal and a couple of vertical layouts, and let’s say those are TextViews.
Suppose the height changes in the TextView, for instance, the parent is measured. Which means then we ought to measure everything again, that one is probably pretty fast at this point because it’s cached. But then you tell the parent, then this one, this one, and it continues until you are done.
The reason we need to care about layout speed is because you need to have your layout upwards in less than 15 minutes for a smooth UI. And if you have a lot of those changes in the frame, it’s going to result in a lot of those measures. The deeper the hierarchy, the more explosive the situation becomes and you have really a lot of measure pass.
About a layout pass versus a measure pass: layout occurs once you know to put the dimension of those widgets and the position. The measure is asking all those different widget that you manipulate to measure themselves. That’s very costly. If you’re measuring a TextView, you have to layout the entire text. The measure is a costly operation in some ways more than the layout itself. I really encourage you to measure your layout and make sure that you’re well within budget.
Concerns About the Tool (8:14)
You may have concerns, for example, about the performance. At the moment, it’s roughly the same as RelativeLayout if I do an equivalent layout in RelativeLayout, and when I do the same layout in ConstraintLayout - it’s about the same or faster.
You do get the most gains if you manage to do a flat hierarchy. If you find an example of a ConstraintLayout that is slow, please send it to us. I am confident that we can make it faster, as there’s still a lot of optimization to do.
What’s this measure stuff? It’s similiar to RelativeLayout, yet RelativeLayout can be slow. We tell people it can be slow because it always does two layout passes. In our case, we don’t do that; we issue a setting instead. What that means is that if you’re Wrap Content, the widget that has a set dimension will do a single measure pass of that widget. If you have Match Constraint, we will do two measures passes.
How does it work? At the base, we have linear equation solvers. Something where you specify your linear equation with an associated error possibly. You put it into that solver and the solver gives you an answer.
For the solver we actually do slightly different things. We know of direct restoration. Because we have this constraint model, we can in many situations, identify past situations where we can just directly solve it. And in that case, we really double or triple the performances of the rest of the system.
The reason why we are working on the Layout Editor is because we think we can do something that let’s you go faster than just writing the XML. That being said, writing the XML is actually pretty fast if you’re used to it. XML is not going away, and you will still have XML. Moreover, you can actually use ConstraintLayout in XML, and it’s not much harder than how it was used in RelativeLayout.
Side Constraints (14:45)
Using side constraints, you can indicate that you’re going to move one side of the widget to a target. This is similar to RelativeLayout. You also have a baseline used for alignment. The baseline is the bottom of the text, so you can align two buttons of different sizes, but the text would be aligned vertically. The way you do this in XML is easy: you start with layout of constraint from where you start the constraint and determine what you want to constrain to the target.
What the constraint does in that case, is to have the side that you constrain to be at the same position to the side that is your target. You can use either an ID of an element as a target or the keyword Parent. You connect on the Parent data, and it will automatically use the Parent.
Center Constraints (16:15)
The thing that’s nice with ConstraintLayout is the idea of the Center Constraint. What happens when you set two constraints on the left and the right side of the same widget? They can’t be both right.
What we do in that case is we center it. In this case, there’s a notion of bias, which by default is at 50%, resulting in centering widget. You can move it and change it to anything else, which let’s you build a UI that can be more flexible.
There are also Dimension Constraints. You can specify a fixed dimension or use Wrap Content to have the widget dimension itself. But do not use Match Parent, as it’s not supported Match Constraint is what you should use instead. You can specify a ratio, in addition to a max width and height on the ConstraintLayout itself.
Match Constraint. Match Constraint means that instead of using Wrap Content or a fixed dimension, you put zero DP, much like in linear layout. The way this works is it’s going to size the widget to match those constraints. If the widget was the Wrap Content, by setting Match Constraint, it’s going to expand. You can also specify the margin and it will be the size less than the margin.
For ratio, the one thing you need to do is set one of the dimensions to Match Constraint. When you want the dimension to be set by the solver, put zero DP and you can also specify a ratio. You should specify this attribute with only one dimension to be on Match Constraint.
GONE and Chains (19:29)
A lot of people don’t know about View.GONE. When you have a View A and a View B that is constrained to A, but we want to mark the View A to GONE. This is actually what happens: We remove A, but still use it in the equation. A was just reduced to a single point, and will just slide out. The margin that you had between A and B might have been what you wanted when A was here, not if A disappeared.
You can specify a GONE Margin to something that would specify the value you want. In that case, when the target you’re constrained to is marked as GONE. A powerful recent featrue related to this is Chains. A chain is recognized as being bidirectional constraints. The constraint that goes in opposite directions between at least two widgets.
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" tools:layout_editor_absoluteY="10dp" app:layout_constraintRight_toLeftOf="@+id/button2" app:layout_constraintLeft_toLeftOf="parent" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" tools:layout_editor_absoluteY="10dp" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toRightOf="@+id/button1" />
The way you accomplish this in XML is not visually obvious, but you can see it right to left of button. The left is on Parent, and you get the right on the Parent, but the left goes to the right of the other button. You can see you have two constraints in opposite directions.
Now that you have a chain, you can do a couple of interesting things. First, in the head of the chain, if you set specific attributes, this will drive the behavior of the chain. Those are the current chain styles that we do support. Simply by creating your chain and specifying the style like spread, spread inside, widget all packed, you can have those different behaviors.
Widgets are a little different in that whatever style is specified, the widget will open. In that case, B and C are zero DP and the default behavior on the widget is just to split the available space equally. You can have one widget being more than the other.
The one chain style that’s nice is packed. You can still apply bias so that you can think of chains as giving you group-like behavior like Linear Layout. The difference is that it gives you that behavior on a similar axis. That allows you to create UI’s that are or maybe before you would have had to use something like Linear layout.
app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintHorizontal_chainStyle="packed"
The idea of Guidelines is that we could have things that helps you create a UI. And you could think about it as being in the Editor to help you, but they don’t automatically end up in the View hierarchy at the end. In the particular case of Guideline, it’s just completely gone in the final pass and it’s not even laid out. Here’s an XML example.
<android.support.constraint.Guideline android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/guideline" app:layout_constraintGuide_begin="20dp" android:orientation="vertical" />
The last thing I want to go over in Constraint Layout is ConstraintSet.
ConstraintSet mConstraintSet1 = new ConstraintSet(); // create a Constraint Set ConstraintSet mConstraintSet2 = new ConstraintSet(); // create a Constraint Set mConstraintSet2.clone(context, R.layout.state2); // get constraints from layout setContentView(R.layout.state1); mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main); mConstraintSet1.clone(mConstraintLayout); // get constraints from ConstraintSet TransitionManager.beginDelayedTransition(mConstraintLayout); mConstraintSet1.applyTo(mConstraintLayout
The idea is that you have this flat layout. One of the really nice things with flat layout is it makes animating stuff a lot easier. If you’re animating things or if you’re trying to animate things on a really deep nested hierarchy, you might have all kinds of issues. If you have something that’s flat, animation in linear will work much better.
ConstraintSet allows you to go a step further. What it does is it captures the set of constraints and you can keep the data around.
I can just take lets say a button and drag it. First of all, one thing you can notice is that I can just drag it anywhere. Right now, it’s not Constraint. In fact, if I go into the xml file, you’ll see that the Editor is complaining because it’s not constraint.
You may also have heard about the blueprint mode. You can think about the blueprint mode as like an x-ray mode. It’s just takes your current view and shows what’s relevant for the layout. In a sense it’s a visual representation of the xml attributes. And it’s one to one.
Personally I really like the blueprint mode because I think it makes things a lot clearer in terms of what’s actually necessary. You can also use the design mode. By default we don’t show the constraint unless you hover.
I’m hoping you will get a bit of the way we thought about this tool. It’s really something where there is not a one single way to use it. We are trying to provide different paths and avenues for you to use the tool as you want.
If you’re really an efficient user of XML you can still use the XML and you can have this preview pane here. The preview pane is actually more than a preview pane, as it’s the full-blown Editor, allowing you to change things.
What’s next? / Conclusion (36:33)
That was a very quick overview on what’s next. We probably want to do a lot more with flexibility. If you try the Conversion Tools now, it allows you to take an old layout and convert it.
Please file bugs, and don’t hesitate to contact me.