Android Adventure: Constraint Views with Flow

Hasina H. R.
4 min readSep 28, 2023

--

Image found on css-tricks

Certainly all Android app developers have already used RelativeLayout, LinearLayout, FrameLayout and ConstraintLayout as a container layout when implementing an app’s UI with XML. These containers help a lot to arrange or to position properly how View elements can be shown to an app’s user. Specially the ConstraintLayout can constrain Views to have relation to each other in an optimal way, even if the view hierarchy is complex. Nested view groups are not necessary, so called Flat hierarchy. Thanks to Google Android developer for this handy tool!

So let focus on ConstraintLayout by implementing an appointment schedule board user interface like you see below:

Appointment schedule board user interface

By analyzing this mock-up, we have a list of dates and each date has a list of random available hours. It’s straightforward to guess that the parent View of the layout is a RecyclerView with LinearLayoutManager in vertical orientation. But what about the group/list of hours? You may think to use, as parent View:

  • LinearLayout with horizontal orientation. However, when no more space is available, it is not responsive to put its View children on a new row.
  • RecyclerView with GridLayoutManager. But the latter needs the exact number of columns that cannot be static for our sample due to different device screen’s size and resolution. The number of columns becomes tricky to compute. It depends on the width of the device’s screen, the width of hour’s View, the horizontal margins,…

Let see another way to resolve the problem.

Have you already heard about CSS Flexbox used by front web developer ?(Don’t be troubled, we won’t use WebView or CSS as solution… 🤪). The w3schools says:

makes it easier to design flexible responsive layout structure without using float or positioning.

We need the same feature in the Android View system. Good news! A Google’s library FlexboxLayout brings the similar capabilities. Even more, starting from the version 2.0 of ConstraintLayout’s package, Flow VirtualLayout was added as a widget helper to position referenced widgets similarly to a chain. Setting the “flow_wrapMode” to “aligned” value resolves the problem of showing dynamically the list of hours in the schedule board through rows and columns. So no need to import another library if ConstraintLayout’s package is already available in the project.

The item’s layout of the main RecyclerView looks like:

As you see in the code above, the Flow element is declared inside the ConstraintLayout with some attributes “flow_xxx” (see the official doc for more details on these attributes) to chain rows and columns with gaps. The attribute “constraint_referenced_ids” is not used because each hour’s View will be added in the ConstraintLayout and will be referenced dynamically. And it is done inside the RecyclerView’s Adapter, precisely in the ViewHolder:

The interesting part is the method calls at the line 41 and 42 where all views of hour are removed and added when “ScheduleBoardAdapter.onBindViewHolder” is called:

  • In “updateByRemovingHourViews” method, each hour’s View is firstly removed from the Flow then removed from the ConstraintLayout.
  • In “updateByAddingHourViews” method, each inflated hour’s View is firstly added to the ConstraintLayout then added to he Flow. Note that Flow requires View with a reference id. In the line 71, this id is generated.

The data model is pretty simple:

The hour’s View layout:

We can say that it is enough to show the schedule board, however removing/adding all Views of hour is not efficient each time the RecyclerView binds item. As RecyclerView recycles items when an user is scrolling, we will also recycle hour’s View and use a garbage collector. The Adapter implementation becomes:

What’s changed:

  • With the implementation from the line 45 to 108, the hour Views is now recycled. How ? From the line 45 to 50, the size difference between already inflated hour Views (A) and the hours to be shown (B) determines the recycling strategy. If the (A)’s size is the same as (B)’s size, no need to inflate or to remove an hour’s View; (A) is updated with (B) by “updateHourViews” method. In case (A)’s size is greater, the exceeding Views are removed and the remaining are updated with (B) by “updateByRemovingHourViews” method. Inversely, more Views are inflated and (A) is updated with (B) by “updateByAddingHourViews” method.
  • At the line 17, the “hourViewGarbageCollector” garbage collector is declared as Deque. Used as LIFO stacks, “updateByRemovingHourViews” method adds all removed hour Views in it (at the line 70) and “inflateHourView” method returns from it these collected View instances, when available (from the line 103 to 105). Like so it reduces hour’s View to be inflated when “updateByAddingHourViews” is called.

It’s done! We have a working Adapter for the RecyclerView of the schedule board screen, I let you to implement the Activity or Fragment. A future post may talk about how to implement this schedule board screen with Jetpack Compose 🚀.

See you soon and happy coding! 🎉

--

--