Aspect Ratio in Staggered LayoutManager using Constraint Layout

Hello Developers !!! Today we are going learn a efficient way to maintain aspect ratio of our View in Staggered Layout using Constraint Layout.We will take the example of Pinterest app design.This article assumes that you already have basic knowledge of LayoutManager and ConstraintLayout if not than i recommend to read the following link before going further

This is our goal to design like Pinterest layout.For this we need a StaggeredGridLayoutManager in RecycleView

What is StaggeredGridLayoutManager ?

As per doc Staggered layout manager is a LayoutManager that lays out children in a staggered grid formation. It supports horizontal & vertical layout as well as an ability to layout children in reverse.

In Staggered Layout the Aspect Ratio of each item in list might be different and it will get more complex when we try load image from a url. So while loading image from url we don’t know the exact aspect ratio of the image.So what we do is , Keep the image width as match_parent and height as wrap_content in our row layout , so whenever the image is loaded, it will automatically wraps its height as per the image width

The Problem ?

We know that some time internet connection might be very slow which takes a lot of time to load the image,So in normal scenario we use a placeholder image while loading the actual image to make user experience better.

But in Staggered Layout as we don’t know the aspect ratio of image until its fully loaded and also our placeholder aspect ratio might be different than the actual image ,So Initially while loading images in list we see all the item in placeholder aspect ratio and when the actual image is loaded completely its starts to set the the actual image with its own aspect ratio ,This makes the user experience bad because when user scrolls the list ,than the item changes its height/width very frequently

Don’t need to worry, we can now solve this using constraint layout aspect ratio feature.But first you need to know how aspect ratios works in Constraint Layout

Aspect Ratio in Constraint Layout

Aspect Ratios let you accomplish roughly the same thing as a PercentFrameLayout, i.e. restrict a View to a set width to height ratio, without the overhead of the additional ViewGroup in your hierarchy.

Setting a ratio on a view

To set a ratio for any view inside a ConstraintLayout:

  1. Make sure at least one of the size constraints is dynamic, i.e. not “Fixed” and not “Wrap Content”.
  2. Click the “Toggle aspect ratio constraint” that appears in the top left corner of the box.
  3. Input the desired aspect ratio in width:height format, for example: 16:9

For More Detail on Constraint Layout you can read this great article

The Solution

As we’ve discussed the problem above , one solution that developer came across is to fetch the height/width of an image directly from the back-end API and set the placeholder ratio fetched from the back-end API , which will lead to a smooth user experience when user scrolls the list

But before the constraint layout came into the picture developers used to do a lot of work to set the height/width on image manually

For that first we need to calculate the image width based on device size and span count than convert that px value to dp and than set the height/width of image programmatically on onBindViewHolder() and while calculating the width of the view you need to also consider the padding.

This was a lot of manual work but we have constraint layout now we can directly set the ratio on image view and the constraint layout will take care of all that

Let see an example

First we will build our row_poster.xml

In above layout we have set the ImageView width/height to 0dp which is match_constraint in Constraint layout which means its behave as match_parent.

By default we kept the app:layout_constraintDimensionRatio="1:1" which means our Image will be square by default

Now here we have MoviePoster Model

class MoviePoster(var name: String,
var imageUrl: String,
var width: Int,
var height: Int)

Which has width and height of image url fetched from webservices

By default we kept the app:layout_constraintDimensionRatio="1:1" which means our Image will be square.Now will set the aspect ratio of each image programatically.

This is how we set the aspect ratio of an image using constraint layout,We will create a global ContraintSet() object and use it onBindViewHolder() like this

class MoviePosterAdapter : RecyclerView.Adapter<MoviePosterAdapter.ViewHolder>() {

private val set = ConstraintSet()

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val poster = mMoviePosters[position]

holder.mMovieName.text = poster.name

Glide.with(holder.itemView.context)
.setDefaultRequestOptions(requestOptions)
.load(poster.imageUrl)
.into(holder.mImgPoster)
    val ratio =String.format("%d:%d", poster.width,poster.height)
set.clone(holder.mConstraintLayout)
set.setDimensionRatio(holder.mImgPoster.id, ratio)
set.applyTo(holder.mConstraintLayout)
}

We will clone the current constraint of the view in ContraintSet() object and than will set the dimension ratio in “width : height” format and provide the id of the view on which we want set this ratio i.e in our scenario is ImageView’s id

And last we apply the changed constraint from set object to our Constraintlayout

That’s it :)…That’s all we need to do…Constraint Layout will manage all the stuff

You can checkout this example on GitHub. Since the Kotlin is our new java this example is written in Kotlin

Thank you !!!

If you find this article helpful. Please like,share and clap so other people will see this here on Medium. If you have any quires or suggestions, feel free to hit me on Twitter.