3

I have a tricky problem related to synchronized scrolling of two different views. I've made my own custom grid view widget, which has "sticky" views to the left and top that only in one direction with the grid. Think of a calendar where you have times at the top, dates at the left, and when you scroll horizontally through time, the date view should stay put, and when you scroll vertically through the dates, the time view should stay put.

The grid itself is implemented using a nested horizontal scrollview in a vertical scrollview. The grid is working great, so no problem there. Since the sticky views are not in the actual grid, I have overriden onScrollChanged in the grid scrollviews and programatically call scrollTo on the sticky views when the user scrolls the grid.

That works as expected, except that there is a slight time offset as to when the two different views start scrolling and ends scrolling. It makes sense when you consider that the scrolling likely is executed linearly on the UI thread I suppose..

All the views are scroll views, and I have enabled smooth scrolling and used smoothScrollTo, etc, instead to try to improve this, but it's the same problem nonetheless. The problem is especially noticeable on larger screens, such as the Samsung Galaxy Tab, whereas it's hardly noticeable on small-medium screen devices.

Any help is appreciated! If there is an easy fix, great..if it means new design (that meets the sticky view usecase above), then so be it.

Code to trigger prog. scroll, same for horizontal

@Override  
protected void onScrollChanged(int x, int y, int oldx, int oldy) {  
   mListener.onScrollY(y);  
   super.onScrollChanged(x, y, oldx, oldy);  
}  
// which leads to,  
// Handle vertical scroll  
public void onScrollY(final int y) {  
   mCurrentY = y;  
   mVerticalScroll.smoothScrollTo(0, y);  
}  

XML layouts below, if that's of any help

The actual grid, which is a horizontal scroll view wrapped in a vertical scroll view and the grid items are added vertically in the nested linearlayout
>

  < com.....VerticalScrollView  
    android:id="@+id/gridscroll" 
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" 
    android:layout_below="@id/timescroll"
    android:layout_toRightOf="@id/vertscroll"  
    android:layout_alignTop="@id/vertscroll"  
    android:layout_marginLeft="2dp" android:scrollbars="none"  
    android:fadingEdge="none">   

    < com....HorizScrollView
    android:id="@+id/horizscroll"
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"
    android:scrollbars="none"  
    android:fadingEdge="none">  

    < LinearLayout android:id="@+id/grid"  
      android:layout_width="fill_parent"  
      android:layout_height="fill_parent"  
      android:orientation="vertical">  

      < /LinearLayout>  

      < /com.....HorizScrollView>  

      < /com.....VerticalScrollView>  

The horizontal sticky view

 < com.....GridTimeScrollView
    android:id="@+id/timescroller" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scrollbars="none"
    android:fadingEdge="none">

    < LinearLayout android:id="@+id/timelist"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" />
    < /com.....GridTimeScrollView>

The vertical sticky view

< com....GridVertListScrollView
android:id="@+id/vertscroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none" 
android:fadingEdge="none">

< LinearLayout
android:id="@+id/vertitemlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" />
< /com.....GridVertListScrollView>
asish
  • 4,767
  • 4
  • 29
  • 49
Andreas
  • 539
  • 1
  • 6
  • 13

3 Answers3

6

First of all, I think you should be aware of this: ScrollView Inside ScrollView

In short: using scrollviews inside scrollviews is a bad thing that breaks many optimizations.

Now, onto your question.

I've had a similar need to what you described. I ended up implementing a custom view and its onDraw method. This was in part because I was drawing something not trivial and you may not have to do it.

Anyway, I believe that your best option is:

  1. Implement a custom view that extends relative layout
  2. create the layout of this view with the top, left and "main" views that will be the scrollable components
  3. add a OnGestureListener to this view and pass touch events in your activity into the custom view
  4. when your gesture listener detects a fling or a scroll, invoke scrollBy in each of the scrolling views. When you do this, if you want the top view to scroll horizontally only, pass 0 as the vertical scroll distance.
  5. In order to implement smooth fling movements, you need to create a scroller (in your custom view). When the gesture listener detects a fling event, set the scroller up. Then, override your custom view's computeScroll() method and update the scroll in each of child views. Check this example to know how to implement it. I apologize, I will try to post a better example when possible. Check my code below... it's simpler :)

Update: sample code

@Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            if (!scrolledLastFrame) {
                lastX = scroller.getStartX();
                lastY = scroller.getStartY();
            }

            int dx = scroller.getCurrX() - lastX;
            int dy = scroller.getCurrY() - lastY;

            lastX = scroller.getCurrX();
            lastY = scroller.getCurrY();

            doScroll(dx, dy);
            scrolledLastFrame = true;
        } else {
            scrolledLastFrame = false;
        }

    }
Community
  • 1
  • 1
Pedro Loureiro
  • 11,436
  • 2
  • 31
  • 37
  • Thanks for your answer. The post you refer to seems to discuss the combination of scroll views with list views though? I do realize that the nesting of scroll views probably isn't optimal, but it seemed like the only alternative that didn't require a new widget from scratch. Before I consider this question answered though, I wonder how this guarantees more "synchronized" scrolling than my current solution? Won't I still have to scroll one view (grid, sticky view) at a time, that may cause a slight offset in how they are scrolled? – Andreas Jan 20 '11 at 09:37
  • No. I think that your "sync" problem is because when you get the information about the scroll, that has already been processed by one view and then you order the others to scroll alike the first one. This way, one view scrolls and the others will do the corresponding scroll in the next cycle. With my approach, all views scroll at the same time. And you won't have that problem. – Pedro Loureiro Jan 20 '11 at 12:41
3

Don't use smoothScrollTo, use scrollTo instead. smoothScrollTo will animate a scroll from its current position to the position you want, because you want them to be synced, you want the other scrollview to be instantly exactly where the other scrollview is, scrollTo does that.

jsonfry
  • 2,067
  • 2
  • 15
  • 16
2

Looking at the answers. Why don't you handle it with a OnTouch Listener. When the touch event is MOVE you call.

public void onScrollY(final int y) {  
   mCurrentY = y;  
   mVerticalScroll.smoothScrollTo(0, y);  
}  

with the value of getVerticalScroll on your other ScrollView. That's pretty easy. And return true so TouchEvents will be handled further.

Here's what it could look like

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE:
            lv2.onScrollY(lv.getScrollY());
            break;
        default:
            break;
    }
    return false;
}

Simple right?

Not sure if it's exactly what you wanted. But could you elaborate exactly what you're trying to achieve. Not in the sense of layout but in practice..whats the point of your UI?

Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
  • Thank you for the answer. This might be a feasible workaround, but it seems that I have to choose another implementation for other reasons now as well. – Andreas Jan 21 '11 at 22:00