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
The series will cover small topics and will add incremental value to the same GitHub repo:
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:
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 bematch_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:
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:
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.