At first logical solution was to use vertical LinearLayouts inside a ScrollView - easy and simple.
All I had to add is code which adds elements proportionately in each LinearLayout.
<ScrollView> <LinearLayout orientation="horizontal"> <LinearLayout android:layout_weight="0.5" orientation="vertical"> <LinearLayout android:layout_weight="0.5" orientation="vertical"> </LinearLayout> </ScrollView>This worked fine at the beginning but when I added more than 100 items, as expected, app started to crash. Well if you think about it it was obvious, with this approach we don't use recycling so layout constantly had 100 items filled with layouts, pictures, formatted text..
Second solution I thought of was two synchronized ListViews. They have internal caching so all I had to do is synchronize them.
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" android:paddingLeft="10dp" android:paddingRight="10dp"> <ListView android:id="@+id/list_view_left" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:paddingRight="5dp" android:scrollbars="none" > </ListView> <ListView android:id="@+id/list_view_right" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:paddingLeft="5dp" android:scrollbars="none" > </ListView> </LinearLayout>My first attempt was to add OnTouchListener which will pass the touch event to the opposite list and OnScrollListener which will update first opposite child.
listOne.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent arg1) { listTwo.dispatchTouchEvent(arg1); return false; } }); listOne.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView arg0, int arg1) { } @Override public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) { if (l1.getChildAt(0) != null) { Rect r = new Rect(); l1.getChildVisibleRect(l1.getChildAt(0), r, null); l2.setSelectionFromTop(l1.getFirstVisiblePosition(), r.top); } } });Well this seemed as good solution. It was working with single list, scrolling was smooth so adding the same code to the opposite list should work fine but unfortunately it didn't. They ware both synced on first visible child so when one would disappear the other would automatically be positioned to match the new one. This made scrolling feel quite unnatural so other solution had to be found.
It occurred to me that I can just calculate distances from top of both elements, subtract one from another and add offset of the current element. Hmmm, well let's be more precise it took me a while get to this calculation but if you draw an example yourself, you'll see its just logical.
Source can be found on Github
This solution works fine. I have few bugs to fix, simplify the code - make it more simple to use and that should be it.
Still it bothers me if this can be implemented by using BaseAdapter and AdapterView. I'll have an option to use adapters caching and what remains is child positioning. Well, I'll give it a try as soon as I find spare time...