The mysterious case of the skewed ProgressBar
Yet another instalment in the WTF Files series
Introductory note
I do realise last time the story was very, very long. Too long. Sorry. So this time you’re getting a short anecdote. Five minutes read at most.
Maybe.
Also, the code in here is just meant to give you an idea of how things are done. It’s not been tested (too lazy) and it’s definitely not the code from the project this story comes from (because duh, can’t do that). As a result, stuff you copy-paste from here will probably give you a shitload of errors.
Look at the silver lining: you’ll have to look into things to get them working. And you’ll maybe learn something in the process. Which is not bad, in my book.
One last thing: I’ve been told this article is even better if you read it while having a beer and some peanuts.
What am I ranting about this time?
Lately I’ve been working on another, yet unannounced project. Big client and stuff. Crunching through issues during this week sprint, I ran across some device specific issues. You come to fear that kind of issues, because you know they’re due to something that the OEM has screwed up. Somehow.
And it’s not always possible to fix or work around the issue. And sometimes, when it is possible to “fix” what shouldn’t have broken in the first place, it’s only breaking stuff on all other devices. Not fun. That kind of issue always make me wonder how the hell can those things pass the CTS.
This week’s dilemma was caused by some weird, weird behaviour on a Galaxy SII (Android 4.0.4). Yes, Samsung, of course.
Luckily, this one has an happy ending.
The magical mystery bug
To describe this bug, I’ll begin to show you a mockup of what we were expecting to see. Pretty standard stuff, you’ll agree. That’s an indeterminate ProgressBar with a custom drawable, and a background composed of a simple 9-patch with some padding:
As with the code, these mockup aren’t of course even similar to the ones you see in the app. Can’t use the actual material for more than obvious reasons. Also, I like to fire up After Effects from time to time. But I digress.
How it is done
In case you were wondering, here’s a general idea on how to implement that thing up there. First, you put this bad boy in your layout XML:
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/progress_bg"
android:indeterminateDrawable="@drawable/progressbar"/>
The background drawable is, again, a simple 9-patch with some padding. Not going to show that one.
Second, you have a progressbar Drawable that is simply an XML that spins a progressbar_spinner PNG:
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:duration="@int/progress_spin_period"
android:drawable="@drawable/progressbar_spinner"/>
Yeah, it’s that simple.
A little thing before we go on
I know that you don’t need a nine-patch drawable to create a rounded rectangle. You can and should instead use a shape drawable:
<shape
xmlns:android=”http://schemas.android.com/apk/res/android">
<solid android:color=”@color/whatever” />
<corners android:radius=”@dimen/rounded_radius” />
<padding
android:left=”@dimen/rounded_padding”
android:top=”@dimen/rounded_padding”
android:right=”@dimen/rounded_padding”
android:bottom=”@dimen/rounded_padding” />
</shape>
The thing is that A), those 9-patches are what we have and there’s been no chance yet to change them; and B), the problem here seems to lay more in the handling of the 9-patch with padding in conjunction with the rotate element.
Oh yeah, lastly, the progress PNG that is spun by the rotate element could also be a shape drawable itself:
<shape
android:shape=”ring”
android:innerRadiusRatio=”@int/progress_inner_ratio”
android:thicknessRatio=”@int/progress_thickness_ratio”>
<size
android:width=”@dimen/progress_ring_size”
android:height=”@dimen/progress_ring_size”/>
<gradient
android:type=”sweep”
android:startColor=”@color/progress_spinner_transparent”
android:endColor=”@color/progress_spinner_end”
android:angle=”0"/>
</shape>
But the actual progress thingy in the actual code is different and can’t be obtained with a shape drawable.
Sorry, you can’t get me on this one.
Back to the issue
You see plenty of these. Everyday stuff. You’d think nothing could go wrong with this kind of stuff. Yeah, right.
So, let me introduce you to the magnificent rendition of that impossibly complex piece of UI on a Samsung Galaxy SII:
Awesome, Samsung. Thanks. Now I’m going to kill myself.
Fixing things
I will spare you the whole story of how I got to this, and I honestly have no idea how Samsung could have messed this up in the first place. Let’s just say that getting around it required me leveraging an ability I wish I wouldn’t have to have acquired in the first place: thinking like the en… Samsung. And also some tests to try and isolate the issue. Trial and error sort of stuff, with some lucky hunches.
So, long story short, the way you fix the padding issue is… by declaring the padding in the ProgressBar XML. Yep. True story.
So you just need to change your layout XML:
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/progress_bg"
android:indeterminateDrawable="@drawable/progressbar"
android:padding="@dimen/progress_padding"/>
Our progress bars are back to the way they’re supposed to be, even on the Samsung SII.
Yet another victory against crazy bugs!
What’s next
I promise I’ll try not to focus on a Samsung bug next time. It looks like I hate Samsung… which I don’t. The fact is, they have a lot more bugs in their software than anyone else, and generally these are also weirder.
Anyway, I also have a Sony bug ready that I still have to find a solution to, so it’s in the backlog until it gets sorted out or I have enough for a proper “crazy shit that you can’t fix” kind of story.
Stay tuned!