Horizontal Scrolling inside of a Sectioned ListView with swipe to Refresh in an Android Activity

I’ve been tasked with some pretty challenging requirements for an android activity. It needs to:
– have sections with headers like an iOS UITableView.
– have horizontal and vertical scrolling at once
– have a swipe to refresh functionality, allowing you to swipe down to refresh the data.

Now, perhaps this may be easier to do with a RecyclerViews or maybe some combination of Fragments but when it comes to Android I may be a little old school. I usually work within ListViews and Activities because it just seems to make my life easier.

I’ve been able to achieve all these requirements with my comfort zone with a bit of a mesh of three separate libraries and some custom code, which I shall share below:

The libs:

1- HeaderListView
This is a SUPER implementation of a list view that allows your code to be grouped into sections by using a special SectionedAdapter that makes ListViews so lovely and similar to the Section with Header / Row pattern in iOS.

2- HorizontalListView
This is allows you to create a listview that scrolls horizontally. In order to contain this inside of a vertical ListView or in our case, the Sectioned “HeaderListView”, our sectioned adapter just includes a cell type whose layout simply encloses one of these horizontally scrolling listviews.


Neither of these are actual libraries that you can import into your project, but they are some great implementations of classes that you can use within your projects, so create the classes in your own project and copy and paste the implementations so you are ready to harness their power!

3- android.support.v4.widget.SwipeRefreshLayout
This one is already part of the android SDK and will work with our libraries with just a few changes to them.


NEXT: Code changes!

Those implementations are great, but they won’t play together perfectly.
1- As is, the vertical and horizontal scrolling conflict, so if we try to scroll horizontally in our vertical listview, it will work okay, but as soon as our swipe varies even the tiniest amount in a vertical direction, the swipe will be handled by the vertical list, creating a terrible experience.
2- The header list view will refresh every time we scroll up inside of our Scroll to Refresh view since it is not a standard list view and the proper methods aren’t implemented to tell our refresh view that we can scroll upwards.

To work around these two deal breakers, we just need to add a few lines of code to their classes.

In HeaderListView.java, add a property we can set to tell us if this listview has a horizontal scrolling cell so that we can add some custom code to handle the scrolling conflicts.

// set this to true if the layout contains a horizontal list view and should modify its scrolling interceptor public boolean containsHorizontalListView;

next, in the InternalListView implementation within HeaderListView.java, add this to allow us to temporarily disable the vertical scrolling inside of the listview while the horizontal view is scrolling to the side.

protected class InternalListView extends ListView { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //don't interecept touch events if we are scrolling in a horizontal list view if(HorizontalListView.isScrolling && containsHorizontalListView) { return false; } return super.onInterceptTouchEvent(ev); }

Now we’ll make a few modifications to HorizontalListView in order to make a note that we are side scrolling so that our vertical list view can detect this above

//remember if we are currently scrolling in this view. //this is used to help with simulataneous horizontal/ vertical scrolling views public static boolean isScrolling = false; //remember if we are scrolling inside this view to help manage vert / horizontal scrolling public boolean onTouchEvent(MotionEvent ev) { if(ev.getAction() == MotionEvent.ACTION_DOWN) { isScrolling = true; }else if(ev.getAction() == MotionEvent.ACTION_UP) { isScrolling = false; } return super.onTouchEvent(ev); }


Great! Now if we add a row to our sectioned list view that contains a HorizontalListView, we can just set the flag on the main list view to tell it that it contains a horizontalListView and it’ll allow for a more smooth horizontal / vertical scrolling experience by temporarily disabling vertical scrolling during horizontal scrolling.


Now, just one more change to HeaderListView to allow it to be contained within the SwipeToRefresh Container. As is, anytime you scroll up inside the list view it will refresh. We need to make sure it only refreshes when we are at the top. So add this code to HeaderListView

/* * This method is overridden in order for the SwipeToRefresh to work correctly. */ @Override public boolean canScrollVertically(int direction) { return mListView.canScrollVertically(direction); }



This site uses Akismet to reduce spam. Learn how your comment data is processed.