97

I have 2 ScrollViews in my android layout. How can I synchronise their scroll positions?

4 Answers 4

292

There is a method in ScrollView...

protected void onScrollChanged(int x, int y, int oldx, int oldy) 

Unfortunately Google never thought that we would need to access it, which is why they made it protected and didn't add a "setOnScrollChangedListener" hook. So we will have to do that for ourselves.

First we need an interface.

package com.test; public interface ScrollViewListener { void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy); } 

Then we need to override the ScrollView class, to provide the ScrollViewListener hook.

package com.test; import android.content.Context; import android.util.AttributeSet; import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private ScrollViewListener scrollViewListener = null; public ObservableScrollView(Context context) { super(context); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } @Override protected void onScrollChanged(int x, int y, int oldx, int oldy) { super.onScrollChanged(x, y, oldx, oldy); if(scrollViewListener != null) { scrollViewListener.onScrollChanged(this, x, y, oldx, oldy); } } } 

And we should specify this new ObservableScrollView class in the layout, instead of the existing ScrollView tags.

<com.test.ObservableScrollView android:id="@+id/scrollview1" ... > ... </com.test.ObservableScrollView> 

Finally, we put it all together in the Layout class.

package com.test; import android.app.Activity; import android.os.Bundle; public class Q3948934 extends Activity implements ScrollViewListener { private ObservableScrollView scrollView1 = null; private ObservableScrollView scrollView2 = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.q3948934); scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1); scrollView1.setScrollViewListener(this); scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2); scrollView2.setScrollViewListener(this); } public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) { if(scrollView == scrollView1) { scrollView2.scrollTo(x, y); } else if(scrollView == scrollView2) { scrollView1.scrollTo(x, y); } } } 

The scrollTo() code takes care of any loop conditions for us, so we don't need to worry about that. The only caveat is that this solution is not guaranteed to work in future versions of Android, because we are overriding a protected method.

Sign up to request clarification or add additional context in comments.

10 Comments

-Andy I am also using the code but it is throwing me null pointer expection can you help me
@hemant Can you please point out in which line the NullPointer is thrown and what Android version you are working with?
Hi Andy,thanks for the code.It works great.I would like to know one thing [if you or any1 has ans. to this]- When I fling my scrollView, the onscrollchanged method does not registers all the X and Y coordinates while the scrollview moves. It only gives me the starting position and the last position. Say for ex- I start the fling from Y=10 and leave at Y=30 and then fling velocity takes it to Y = 50 and then stops. So onscrollchanged only registers-perhaps 10, 11, 12..30 and then 49, 50.How can I make it register all the intermediate locations as well and get all the coordinates from 10 to 50??
@Andy I thought protected methods were meant to be overridden.. otherwise just use package private or private.
@Andy hey i have implemented this.its works.but one problem occur.while i come on screen and scroll the data and while i press the second button in this screen i never come on last position of 1 screen .mean i have to press the button and then we got the solution.any idea?
|
12

An improvement to Andy's solution : In his code, he uses scrollTo, the issue is, if you fling one scrollview in one direction and then fling another one in another direction, you'll notice that the first one doesn't stop his previous fling movement.

This is due to the fact that scrollView uses computeScroll() to do it's flinging gestures, and it enters in conflict with scrollTo.

In order to prevent this, just program the onScrollChanged this way :

 public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) { if(interceptScroll){ interceptScroll=false; if(scrollView == scrollView1) { scrollView2.onOverScrolled(x,y,true,true); } else if(scrollView == scrollView2) { scrollView1.onOverScrolled(x,y,true,true); } interceptScroll=true; } } 

with interceptScroll a static boolean initialized to true. (this helps avoid infinite loops on ScrollChanged)

onOverScrolled is the only function I found that could be used to stop the scrollView from flinging (but there might be others I've missed !)

In order to access this function (which is protected) you have to add this to your ObservableScrollViewer

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); } 

1 Comment

What a nice catch, son!
5

Why not just implements OnTouchListener in your activity. Then override the onTouch method, then get the scroll postion of the first ScrollViewOne.getScrollY() and update ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Just another idea... :)

4 Comments

This can work but when you touch and let go the scroll view can scroll for a bit more after you let go which is not captured.
How can i refer / get the refrence of the scrollView ScrollViewOne in this case in java ?
There are also other ways to scroll (onTrackballEvent() for example)
It is a very good idea but instead of getting and setting the scroll y position, it is easier to just dispatch the touch event to the second scrollview. This will also keep the "fling" going in the second scrollview. MotionEvent newEvent = MotionEvent.obtain(event); documentsScrollView.dispatchTouchEvent(newEvent);
3

In the Android support-v4 package, Android provide a new class named NestedScrollView.

we can replace the <ScrollView> node with <android.support.v4.widget.NestedScrollView> in layout xml, and implements its NestedScrollView.OnScrollChangeListener in Java to handle the scrolling.

That makes things easier.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.