Minute of Pain #6: Round corners

Or the difference between Shape and Material Shape

Sometimes some things work really strange. You think “Here it is, I know how to use it” and then boom, you have a bug. But the most annoying thing, when you have a tiny visual bug, that doesn’t affect functionality at all, that even doesn’t really noticeable at first glance. But when you see it and try to understand, what’s going on, you can just dig into it for a good couple of hours, losing your time of producing more functionality needed for business.

Today we not talk about, do you need to be a perfectionist and delay feature release, until you resolve all functional and visual bugs (even minor ones), or you need to find the strength in yourself to say “ It’s time to stop”, or you need to spend your weekends as your hobby to resolve this issue for your own education and experience.

Today we will talk about such issue and how I encountered it. But first look at the picture below:

Hardly noticeable, right? Let’s dive together into the problem statement.

The history

Let’s say we want to make a small chat in our application with the ability to view and share images through it. Some time ago I wrote a couple of articles related to that topic. Please check when you will have time:

  1. How we can create telegram-like uploading progress
  2. How we can create a messenger-like image view

In the latter article, I showed custom image views with some kind of strokes. So the initial my task was to create an image view with rounded corners and a fixed stroke of any color.

“Easy-breathy” you would say. “Just take your Glide library or whatever you use and apply rounded corners formation to it”.

Well, it was not that simple, especially because I had thumbnails, that also should have rounded corners. For some magical reason sometimes my thumbnail was created small enough with desired corners and then it got stretched out, so the corners became bigger.

I admit that maybe it was my mistake in wrongly setting up Glide or didn’t tune it ideally. But I decided to make a more robust solution, which is just clipping rounded corners from the view directly. And we have such view :) CardView.

I didn't want to use CardView, because:

  • Well, I needed just to show a single image. And I thought it will be more optimal to make a simple custom ImageView rather than make a FrameLayout inheritor (CardView) with image view inside.
  • I also wanted to have a rounded stroke wrapping the image, and card view, as far as I know, didn’t allow me to do that, so even if I wanted to use it, I needed to make either custom CardView or invent some workaround (foreground for image view for instance, which is supported only from v23 or even a new third view above image view)
  • I already had custom ImageView so it was easy to add clipping to it.

The problem

So it was decided then. We will add the rounded corners and stroke directly inside our ChatImageView:

Please check the related article, if you want to see the rest of this class.

Now if we will run the program we will see the result, that I showed at the beginning of the article. Seems fine (low quality of image, sorry, but it also looks fine on a device). But sooner or later your eye catches strange small dark pixels (looking like small arcs):

That’s the problem we want to solve.

Why is that? O_o

It really surprising, because the same drawable is used for both clipping the shape of view and drawing the stroke. Moreover in View there is RenderNode which is used to draw view with clipped shape, and it leads to creation of some specific instance of Canvas which apparently supports clipping, but all draw methods are native.

I tried (as I can, but not too much XD) find cpp sources to understand, what happens inside. How clipping actually words, what shape is used for it, or may be it is just a cubic bezier. But unfortunately I failed. Maybe some day I will get the access to that knowledge

Solution

So there is nothing we can do? 🤔

Actually no. I did this simple replacement, and somehow it worked… more or less. I replaced GradientDrawable on MaterialShapeDrawable.

And the result is:

And close comparison the result with GradientDrawable versus MaterialShapeDrawable.

As you may see, the new result is still didn’t fixed the problem fully, but improved the previous old result.

What’s next

To be honest, I don’t know. :(

I would suggest to make the stroke radius one pixel smaller or make the stroke’s rect one pixel more expanded, but those are workarounds, unfortunatelly.

Afterwords

I created this sequence of relatively small articles to show some pain points in the development of the app. It doesn’t necessarily mean that SDK or Framework is bad. Sometimes it is you, who didn’t think about some corner cases or narrow places of SDK. Or most probably it is all: not good documented parts of SDK, your lack of experience or knowledge, and legacy code that needed to be supported.

And sometimes I don’t have a normal solution for the pain I had, just a workaround or “more or less” fine fix.

If you liked that article, don’t forget to support me by clapping and if you have any questions, comment me and let’s have a discussion. Happy coding!

Also, there are other pains, that can be interesting:

Love being creative to solve some problems with an simple and elegant ways