Synchronise ScrollView scroll positions - android
Asked Answered
N

4

97

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

Nagoya answered 16/10, 2010 at 12:41 Comment(0)
T
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.

Telefilm answered 17/10, 2010 at 9:6 Comment(10)
-Andy I am also using the code but it is throwing me null pointer expection can you help meSignificance
@Significance Can you please point out in which line the NullPointer is thrown and what Android version you are working with?Weslee
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??Kucik
@Telefilm I thought protected methods were meant to be overridden.. otherwise just use package private or private.Altorilievo
@Telefilm 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?Baronial
@Innovator-z You provide the ScrollViewListener class yourself, using the code above.Telefilm
@Altorilievo You're right, but I posted the solution in Oct 2010 when Google were still making big changes to the Android API every six months. I should have clarified that I wasn't talking about protected methods in general.Telefilm
@Baronial There aren't any buttons in the example, so I'm not sure what your buttons are doing. Can you create a very simple example class that demonstrates the exact problem you have?Telefilm
@Telefilm I want to know one thing.. how do you get to know the direction of the scroll? i mean wheather it's upward or downwardCassis
@Telefilm I am using this Log.d("scroll",y+":"+oldy); to see value up to which y is scrolled but in Logs, every value of y is not there i.e. y = 149 and then directly to y = 151 ...Can you please tell why is it missing some values? I need to run some code which runs on every 50 units scrolledAdenoid
N
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);
}
Nemertean answered 19/12, 2012 at 17:6 Comment(1)
What a nice catch, son!Irena
B
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... :)

Buxom answered 13/3, 2012 at 18:34 Comment(4)
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.Goalie
How can i refer / get the refrence of the scrollView ScrollViewOne in this case in java ?Kilmarx
There are also other ways to scroll (onTrackballEvent() for example)Arak
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);Hallvard
C
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.

Cubby answered 25/2, 2016 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.