- Android Development: Database Upgrades
- Android Development: Missing API
- (Not) Integrating Application with Intents
- Android Activity Lifecycle
- URI Matching in Android’s IntentFilters
- Android Intent Sender Verification
- Android Jittery Scrolling Gallery
- Encryption on Android & BouncyCastle
- Android Permission Handling
- ContentProvider Curses Cursor
- ContentProvider Wasted Potential
Recently I had a problem with an Android app in which I used a Gallery to scroll horizontally between some items. It worked pretty well, but all of a sudden, without changing the Gallery code at all, it started to jitter weirdly when I scrolled the Gallery.
To be more precise, when I touched the Gallery and slowly dragged my finger to the side, the items would at first follow my finger, only to jump back to their original position. And the moment I moved my finger again, they would follow the gesture again.
If you’ve seen this behaviour in your own code, the following may help.
First of all, you’re probably not using the Gallery wrong. What happened in my case is that I had other views in the Activity, some of which received regular updates, like showing a countdown. If you’ve got any views like that in your Activity, chances are you’re in the same situation I was.
I ended up looking through the Gallery code, and noticed that in it’s onLayout() function it actually clears all its child views, and re-adds the visible ones. At first sight, that seems like a great thing to do, right?
The only problem is that if any other view in same ViewGroup is invalidate()ed, the whole group gets invalidated. I’m not sure that should happen, given this documentation here:
Drawing is handled by walking the tree and rendering each view that intersects the the invalid region. Because the tree is traversed in-order, this means that parents will draw before (i.e., behind) their children, with siblings drawn in the order they appear in the tree. If you set a background drawable for a View, then the View will draw it for you before calling back to its onDraw() method.
Note that the framework will not draw views that are not in the invalid region.
To force a view to draw, call invalidate().
Unfortunately, the Android documentation appears to be a bit vague on how, exactly, the invalid region is defined; I’d normally understand this to be any view or view group “behind” the invalidated view in terms of z-order.
Unless the invalidated view is opaque, in which case there’s probably no need to invalidate other views. But I was updating a TextView, and those are rarely opaque.
As you can see, I’m not entirely sure what is going wrong here, and where. What I do know, though, is that it doesn’t really make sense for Gallery to recreate all its child views while you’re scrolling around in it. And that, at least, is easily avoided.
All you need to do is subclass Gallery and override two functions1:
private long mLastScrollEvent; @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { long now = SystemClock.uptimeMillis(); if (Math.abs(now - mLastScrollEvent) > 250) { super.onLayout(changed, l, t, r, b); } } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mLastScrollEvent = SystemClock.uptimeMillis(); return super.onScroll(e1, e2, distanceX, distanceY); }
What happens, simple, is that when a scroll event is detected, the current timestamp is remembered. If an attempt to call onLayout() is made within a short timespan, the re-layout is ignored.
It’s a hack, yes, but it’s solved the problem. Note that the 250msec I’m waiting here is probably way too generous; on my Nexus One, touch events get fired every ca. 20msec or less if I’m scrolling.
I hope this helps anyone. I’m hoping even more that future versions of Android fix the issue in a less hackish fashion.
Finally, if you want to follow progress on this issue, just go to the bugreport.
- You may need to override more because you’re subclassing a
View, but that’s not really part of this problem. [↩]


