Horizontal scrolling widget based on HorizontalScrollView

Update: Google have released ViewPager, check it out! Also, I’ve done some tweaks to LockingHorizontalScrollView, here’s a post about them.

The effect I’m going after is the same as in Google+ and the new Market app:

  • Several homescreens/streams/dashboard views. One visible at a time, swipe left and right to switch between them.
  • There is no padding around each child view, they take all the space that is allocated to their parent view.
  • Switching views one-at-a-time. Don’t want flinging over 3 views at once.

Turns out Gallery widget is not the best starting point for implementing something like this:

  • It supports padding around each gallery item. Gallery items don’t generally take up all the horizontal space, several can be visible at the same time, next to each other.
  • When user swipes, Gallery flings, potentially over several child elements. Preventing that requires some hacky code.
  • It is adapter-based. Cannot define all the views in singe XML layout, need to create/inflate them in adapter.

Then there’s HorizontalScrollView. It’s much simpler than Gallery so less quirks to hack around. The most prominent feature that Gallery has and HorizontalScrollView lacks, is that gallery always tries to center on one of its elements. HorizontalScrollView can be scrolled freely, and will happily stay at any horizontal scroll offset. But this is not hard to fix. By listening in on touch events and detecting flings, I can make it smooth-scroll to the nearest screen-ful in fling’s direction. I’m saying “screen-ful” not “element” because HorizontalScrollView is a subclass of FrameLayout and can only have a single child view. So I’m putting a horizontal LinearLayout in there, and the child views go in that layout. The LinearLayout is slightly hacked too to set appropriate widths for its child views.

Here’s screencast of updated the demo application:

So two classes of interest in the repo:

  • LockingHorizontalScrollView. “Locking” as in “will lock on to grid
  • WideLinearLayout. “Wide” as in “each child element will be asked something similar to FILL_PARENT

6 thoughts on “Horizontal scrolling widget based on HorizontalScrollView

  1. Very nice work, it’s exactly what I was looking for… But I have noticed a problem while changing screen orientation, it seems like the width of the layout doesn’t change, so it doesn’t fill up the entire viewport anymore… Do you have the same problem?

    1. Hi Dario,
      just tried changing screen orientation with example app, running Android 1.6, 2.2 and 2.3.4 — seemed to work correctly.

      Can you describe in more detail–do you see this problem in example app? What Android version are you using? logcat output would also be useful.

  2. Do you mind explaining how it works? i.e., how does it make each child page as wide as the screen?

    onMeasure() and layout confuses the heck out of me, so looking at the code, it doesn’t look like it should work 🙂 But it does, so I’m stumped.

    1. Hi,

      first off, just in case you missed it, Google have released their implementation:

      onMeasure stuff is tricky indeed. Child views get assigned correct width from WideLinearLayout.onMeasure(). During this call, the component needs to figure correct width of itself. It first sets width of each child element to mScreenWidth, simple and brutal. Then it lets the inherited onMeasure code run, which measures each child and should end up with width of mScreenWidth*getChildCount().

      mScreenWidth is badly named. It’s actually width of the component, which might be less than width of screen.

      1. That’s awesome news. Although since I pretty much already re-invented the wheel, I’m not going to use it 🙂 I’m gonna take a look at their code though.


  3. Oh wait, the google version is based on an Adapter, so your comments apply. (Although, thinking out loud here, it’s probably not that big of a deal to get the adapter to use the XML.)

Comments are closed.