As a versatile mobile software engineer, I consider myself as a pretty lucky guy. I’m working both on iOS and Android platforms. Let’s be honest: I’m spending a lot of my professional and personal time working on Android and trying to spread everything I know about it via this blog, some conferences, some open-source projects, etc. But I’m also very close to the iOS ecosystem and some iOS developers. Talking to them everyday help me remember iOS and Android are not so different eventually. They both have to learn from each other.
Most multi-skilled (and with no preconception) mobile developers will assure you developing on Android is as great and easy as developing on iOS. Almost everything that is possible on iOS can be done on Android and vice-versa. Depending on the problem you are solving, it can be easy to make it work on Android or iOS. For instance, I consider developing adaptive and responsive layouts (I’m talking about adaptive layouts and not fixed/absolute layouts created with Interface Builder1.) in iOS is a pain in the ass compared to Android. This usually results in having 80% of the iPhone applications running only on portrait orientation and having fixed
UITableViewCell heights. On the other hand, animating objects was (prior to Android 3.0 which introduced property-based animations) quite boring and creating smooth custom
Views can be complex on Android because of various reasons (garbage collector, asynchronous measure/layout, UI toolkit sometimes slow compared to iOS, etc.)
I’m often talking to developers or reading some articles on the Internet and I noticed lots of people are complaining about the Android SDK. Most of the time they argue the SDK does not let them do as many things as iOS does. They complain about the fact it is impossible to implement things on Android, that the Android SDK is too poor, that the Android SDK is not permissive enough etc. When it comes to things I love, I can be fairly impulsive so I get angry. It drives me mad because I’m sick of looking at blind people. They are often too lazy or stupid to look at the documentation or source code (RTFM!). Most of the time, they did it on iOS but couldn’t find an equivalent to this technique on Android. I strongly believe their problem is they just don’t get it! They simply have to “think different”. This mantra was the Apple slogan a few years ago but it looks like some iOS developers are relatively new and don’t get the original Apple way of thinking.
You probably wonder why I have decided to write this article. The reason is pretty simple and relies on the new version of Path. Path is a Facebook-like social network. The company recently released a second version of their application. I will be frank with you: the iOS version is astonishing. It’s laggy but in term of UI/UX, I consider it as a reference. It contains a lot of brand new UI patterns that has been designed with pixel perfection. When looking at the iOS app, I decided to update my app on Android and I got mad. Yes, as I said before, I am mercurial when it comes to things I consider important. I really don’t understand why Path decided to publish such a crappy app on Android. Unskilled developers? Few resources? iOS porters? Android aversion/detestation? An aggregate of all those factors? I really don’t know but I consider (and that’s just my own point of view) publishing nothing is better than releasing an application working correctly only on hdpi devices (handling screen densities is so easy thanks to the Android framework – not apprehending the resources switching system indicates a total ignorance of Android itself). Finally, the application is a complete port of the iOS version. They did not create a design that is dedicated to Android – you can have a look at the new Android Design website and especially the ‘Pure Android’ page. To conclude, apart from those two points, Path respected my motto “Do less but do it insanely great”. The application is very poor compared to the iOS version but the available features are working correctly.
In order to show you Android is as capable as iOS I decided to create the UI widget you can see in the Path timeline. Path decided to prioritize the information on screen by displaying the current timestamp only when needed. As a result instead of adding often useless timestamps everywhere, they attached a information panel to the scroll indicator (this is the name given to scrollbars in iOS). This is only visible when the
UITableView is scrolling. Android users can see this as a fast scroller (like the one in the latest People app) but you can’t directly interact on it. The screenshot below gives you an example of the result:
The iOS implementation
In order to look at the implementation, I started looking for open source projects and some websites describing the technique. I finally found a blog post describing how to attach an info panel to a scroll indicator. The problem is this technique is a complete hack. To sum up, it consists on getting the latest subview of the
UIScrollView (for your information, in iOS,
ListView equivalent – inherits
ScrollView equivalent). It appears this subview is currently an
UIImageView used to draw the vertical scroll indicator. Thanks to it, you can determine the bounds of the scroll indicator or directly add a layer to it.
The problem relies on the fact Apple could change the order of the subviews or completely change how scroll indicators are drawn. They could and they have the right to as this is not part of the public API. Let’s imagine, the future release of iOS (iOS 6?) draws scroll indicators manually (with no use of an
UIImageView but directly using a
CGImage), the application would be completely broken and the info panel would be attached to something else, something unknown. When you have millions of users out there, you can’t afford using hacks like this one. If your application is broken, it could take weeks to fix the issue (be notified the application is broken, find a way to bypass the problem, implement the solution, wait for the AppStore review process, etc.).
I don’t know how Path developed it but I hope they did not use this hack and found a greater implementation. Here are the possibilities I can think of:
They have developed their own
UIScrollView: I strongly believe that is not the Path’s option. Designing a
UIScrollViewis extremely complex and would require a lot of time to get inertia-scrolling, edge bouncing working exactly as the framework-based
They found a way to get a reference to the scroll indicator. I don’t know the documentation by heart but I’m pretty sure that’s impossible. Getting a scroll indicator is not part of the API in iOS. The only possible thing you can do is to change the style of a scroll indicator using a predefined enumeration of styles. You know nothing about the actual implementation.
They compute the position of the info panel on their own:
contentSizeproperties. Using those properties in addition to the height of the
bounds.size.height) gives you the ability to determine the position and size of the scroll indicators. I sincerely hope they used this technique and I invite people related to the Path iOS app to write a comment that states they did not hacked iOS at the end of this post.
Here we are: we know it is possible to implement such a UI widget in iOS. So, why have they chosen not to implement it on Android? I don’t know and I think I will never know so we will just suppose they think that is not possible on Android. Because of this they simply removed the scrollbars from the timeline! Actually the framework provides everything you need to do such a thing starting from Android 2.0 (API level 5) (the current minimum requirement for Path Android is 2.1, API level 7)
Implementing a scroll panel on Android
Seeing such an awful version of Path on my Android device, I started thinking about a way to implement an info panel attached to the scrollbars on Android. In the following lines we will simply get an overview of all methods that could be useful to implement such a behavior
Getting the current scrollbar position and size
Where iOS uses a
UIImageView as scroll indicator, Android draws scrollbars using a
Drawable (or, to be more specific, a
ScrollBarDrawable that is not part of the public API). Getting the bounds (using
getBounds()) of the
Drawable would have let us know the current position of the scrollbar within the
View. Unfortunately, the
View API has no
getScrollBarDrawable() method. This reminds me the iOS SDK …
Of course, we could have hacked something more horrible than the first iOS implementation we talked about. We could use introspection/reflection on the
View class to get a
ScrollBarDrawable instance but once again we fall into the public VS private debate and we are not these kind of guys!
The actual technique consists on using:
These methods are used by the system to determine the current position and size to apply to the vertical scrollbar. The only problem we have is those methods are protected and therefore can only be accessed from a class that inherits
View. As a result we will have to extend
ListView in our case) to get those values.
Note: Remember the Java language authorizes methods visibility extension. In other words, it means a protected method can be made public by a child class. In the previous example we could made public all
computeVerticalScroll[Extend|Offset|Range]() methods so they can be used from everywhere.
You may also need
getVerticalScrollbarWidth()) to get the position of the vertical scroll bar within the screen as well as its position within the screen (left or right).
Dismissing the info panel at the appropriate moment
If you look closely to the scrollbars, you will notice they appear instantly once the user starts scrolling. After a delay without user interaction, they fade away. This behavior is a direct consequence of a call to
awakenScrollBars(int startDelay, boolean invalidate). Listening to this method will let you know when to display the scrollbar as well as the delay after which the scrollbars must fade away. Using a very simple
Handler you can schedule a
Runnable that will perform the dismissing animation on your info panel after the given
startDelay. This method has been introduced in API level 5. This is the reason why this overall technique requires at least Android 2.0.
Getting the scrollbar fade duration
A consistent way of dismissing the info panel is to use an animation which duration is equal to the one used when
View is fading the scrollbars away. You can get this duration thanks to the
ViewConfiguration class. This class contains a lot of constants and scaled dimensions that may be used when developing custom
View. To get the scrollbar fade duration you can use the
getScrollBarFadeDuration()) static method. This method is available starting from API level 5 which is not a problem as this is our minimum API requirement.
ListView is scrolling
There are several ways to do it. The easiest way is to consider a call to
awakenScrollBars as an indication the
ListView has been scrolled. It’s okay but I prefer attaching a dedicated
OnScrollListener to the
ListView using the
setOnScrollListener(OnScrollListener) method. This listener will have an
onScroll(AbsListView, int, int, int) method that will be called every time the
ListView scrolls. The only problem with
ListView is you can’t attach several listeners at the same time. If you need to attach several
OnScrollListeners (for instance one from your custom
ListView and another one in your
Activity’s code), a great option is to implement an observer pattern or a forward mechanism.
We now have all the data needed to create our scrolling info panel. The implementation uses no hack at all as all methods we are using are part of the public API. As a result, we are sure the code will run perfectly on future versions of the platform. I have created a demonstration application which screencast can be seen below (I’m sorry for the crappy screencast, but I don’t have the appropriate equipment to do this):
Some of you may ask “Why aren’t you sharing the code?”. Actually the current code I implemented is working like a charm but I don’t want to share it right now. I’m always taking care of the snippets of code I’m sharing and I don’t consider this as proper enough to be shared, especially to developers that may reuse it without thinking about the way it’s working. Moreover, the current implementation only works for Android 3.0 because I was to lazy to animate
Views on my own. I preferred using the new animation APIs. Just note, it would be relatively easy to do it starting from Android 2.0 by manually animating the
In a nutshell, do always consider everything as ‘implementable’ on Android. I haven’t either talked about the Path ‘swipable’ sidemenu (really similar to the one Facebook recently added to their application), but it’s also possible to implement it on Android (I consider the Facebook implementation as very disappointing). I can’t say more about this now but I will keep you informed soon … I assure you Android is a really great and capable OS. You just have to think differently, address issues differently from what you used to. Be open and open-minded! I assure you Android has almost no limit. Stop doing crappy work2, be a perfectionist user and developer, develop greatly, live your apps, create things people will admire, applications people will remember.
Interface Builder is the name of the WYSIWYG editor you can use to create UI on iOS. It layouts UI widgets in an absolute manner.
An approximative translation for an expression of mine : “Arrêter de faire du petit boulot !”. It describes something that should not have been done, something unreasonable in term of quality, something that is not great enough to be a sold/shared as a product.