MVVM Flickr Android app with common dev use cases — Part 2

Yoav Gray
4 min readJan 27, 2020

--

This is Part 2 of a series that will showcase common use cases that i’ve been curious about how to implement as I don’t get to do most of them on my day to day work @Autolist, or some of it was implemented years ago and now is core legacy code that no one wants to touch :)

You can get the end result of this article in this github repository:

The incremental branch is here: https://github.com/yoavgray/flickr-kotlin/tree/part-2

Part 2: Show loaded images in squares using ConstraintLayout

As a reminder, this is what the app looked like at the end of Part 1:

Screenshot of what the app looked like at the end of part 1

There are a few issues here:

  • We want to show these images in perfect squares, no matter how many columns we decide to show.
  • We do not want the photos to look stretched out.
  • If we set the the height on the ImageView and its wrapper to be match_parent , it will stretch out to take the entire screen as the parent is the root view.

Step 1 : Resize all images to be the same size and use a centerCrop() transformation

Ideally, when fetching images from our own server, we would describe the size of the image that we’d like to fetch so we wouldn’t have to transform them on the client. In this case, we’ll have a performance hit, but there are ways to work around it.

When using Picasso, you can resize and scale the images you’re loading. If before, we called:

Picasso.get().load(photo.url).into(itemView.imageView)

We can call this instead:

Picasso.get().
load(photo.url)
.resize(IMAGE_SIDE_PX, IMAGE_SIDE_PX)
.centerCrop()
.into(itemView.imageView)

And it already looks much better:

Here’s a good link to read more about it, and you can also check Picasso’s documentation.

Step 2: Use ConstraintLayout to make all images be perfect squares.

One way of doing this which is a little bit more involved, is to actually measure the screen width with DisplayMetrics and then set the size you’d want the images to be according to how many columns you’re showing, but then you need to calculate margins correctly or else some images will wrap to the next row and it can get messy.

A better option is wrapping the ImageView with a ConstraintLayout that wraps its content. Inside it, edit the ImageView to be:

<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>

And that’s it. All the images will be loaded into perfect squares:

Let’s add some spacing and we’re done

Part 3: Add spacing

As for spacing, there are multiple ways of doing that. You can add an ItemDecoration to orchestrate the spacing between the images and you can try and calculate if an image is at the top/right/bottom/left, but that’s just unnecessarily complicated.

What I found to be the easiest way is to add padding around the entire recycler view, and then add the same amount of padding around each image, so that if the image padding is 4dp, the padding between all the images themselves will be 8dp and then between the images and the margins it will be 4dp and that’s where the recycler view padding of 4dp comes into play, and that’s what it looks like.

The changes are:

In activity_photos.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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"
tools:context=".ui.PhotosActivity"
android:padding="4dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photosRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>

In photo.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

And the end result is:

End result of what the app should look like with the proper spacing

And that’s it. In the next part, I’m going to add a search EditText at the top that will run a free text search query to the same api, and we’ll see how to debounce it with Coroutines so that we don’t make an API request for every letter added to or removed from the EditText .

If I missed anything important, please leave a comment. Got questions? Follow me on Medium at Yoav Gray or find me on Twitter.

--

--

Yoav Gray

Android Eng @Uber | Previously @Autolist (Acquired by @CarGurus)