Sync Two ScrollViews

Situation: I have two ScrollViews inside each of two HorizontalScrollView Row tables.


Purpose: When I touch the drag of one of the ScrollView, the other ScrollView should scroll as much. For example, if I have a list of the name on the left of the ScrollView and the corresponding phone numbers in the right ScrollView, scrolling one ScrollView should not break the original restriction between names and phone numbers.

Can it be implemented onTouchEvent? If so, how do I implement this (on both or on one of the ScrollView)?

Please help me with an Android guru!

+6
android android widget
source share
2 answers

I have a simple solution that works for me:

  • subclass ScrollView and override their onScrollChanged event to update the ScrollManager when scrolling changes:

     public interface ScrollNotifier { public void setScrollListener(ScrollListener scrollListener); public ScrollListener getScrollListener(); } public class SyncedScrollView extends ScrollView implements ScrollNotifier { //... private ScrollListener scrollListener = null; @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (scrollListener != null) scrollListener.onScrollChanged(this, l, t, oldl, oldt); } @Override public void setScrollListener(ScrollListener scrollListener) { this.scrollListener = scrollListener; } @Override public ScrollListener getScrollListener() { return scrollListener; } } 
  • Create a ScrollManager class that coordinates the scrolling of multiple participants.

     public interface ScrollListener { void onScrollChanged(View syncedScrollView, int l, int t, int oldl, int oldt); } public class ScrollManager implements ScrollListener { private static final int SCROLL_HORIZONTAL = 1; private static final int SCROLL_VERTICAL = 2; private ArrayList<ScrollNotifier> clients = new ArrayList<ScrollNotifier>(4); private volatile boolean isSyncing = false; private int scrollType = SCROLL_HORIZONTAL; public void addScrollClient(ScrollNotifier client) { clients.add(client); client.setScrollListener(this); } // TODO fix dependency on all views being of equal horizontal/ vertical // dimensions @Override public void onScrollChanged(View sender, int l, int t, int oldl, int oldt) { // avoid notifications while scroll bars are being synchronized if (isSyncing) { return; } isSyncing = true; // remember scroll type if (l != oldl) { scrollType = SCROLL_HORIZONTAL; } else if (t != oldt) { scrollType = SCROLL_VERTICAL; } else { // not sure why this should happen isSyncing = false; return; } // update clients for (ScrollNotifier client : clients) { View view = (View) client; // don't update sender if (view == sender) { continue; } // scroll relevant views only // TODO Add support for horizontal ListViews - currently weird things happen when ListView is being scrolled horizontally if ((scrollType == SCROLL_HORIZONTAL && view instanceof HorizontalScrollView) || (scrollType == SCROLL_VERTICAL && view instanceof ScrollView) || (scrollType == SCROLL_VERTICAL && view instanceof ListView)) { view.scrollTo(l, t); } } isSyncing = false; } } 
  • create custom ScrollView and set ScrollManager for notification on

     private void setupScrolling() { ScrollNotifier view; ScrollManager scrollManager = new ScrollManager(); // timeline horizontal scroller view = (ScrollNotifier) findViewById(R.id.epgtimeline_container); scrollManager.addScrollClient(view); // services vertical scroller view = (ScrollNotifier) findViewById(R.id.epgservices_container); scrollManager.addScrollClient(view); // content area scrollers view = (ScrollNotifier) findViewById(R.id.epgevents_container_inner); scrollManager.addScrollClient(view); view = (ScrollNotifier) findViewById(R.id.epgevents_container_outer); scrollManager.addScrollClient(view); } 
+12
source share

Thanks to andig for such a wonderful synchron scrollview solution .

But there is a slight lag between both scroll lists during a U-turn.

So, here I am writing an advanced solution to eliminate this delay between scrolling.

I just used the OverScroller class and manually handled the fling event in SyncedScrollView.

You just need to replace SyncedScrollView with the code below. Use other classes from andig solution

 import android.content.Context; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.OverScroller; import android.widget.ScrollView; /** * Created by mitul.varmora on 11/7/2016. * SyncedScrollView * https://stackoverflow.com/questions/3527119/sync-two-scrollview */ public class SyncedScrollView extends ScrollView implements ScrollNotifier { private ScrollListener scrollListener = null; private OverScroller scroller; private Runnable scrollerTaskRunnable; public SyncedScrollView(Context context) { super(context); init(); } public SyncedScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SyncedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { scroller = new OverScroller(getContext()); scrollerTaskRunnable = new Runnable() { @Override public void run() { scroller.computeScrollOffset(); smoothScrollTo(0, scroller.getCurrY()); if (!scroller.isFinished()) { SyncedScrollView.this.post(this); } else { //deceleration ends here, do your code ViewCompat.postInvalidateOnAnimation(SyncedScrollView.this); } } }; } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (scrollListener != null) scrollListener.onScrollChanged(this, l, t, oldl, oldt); } @Override public ScrollListener getScrollListener() { return scrollListener; } @Override public void setScrollListener(ScrollListener scrollListener) { this.scrollListener = scrollListener; } @Override public void fling(int velocityY) { scroller.forceFinished(true); scroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, getChildAt(0).getHeight()); post(scrollerTaskRunnable); } @Override public boolean onTouchEvent(MotionEvent ev) { // return super.onTouchEvent(ev); boolean eventConsumed = super.onTouchEvent(ev); if (eventConsumed && ev.getAction() == MotionEvent.ACTION_UP) { if (scroller.isFinished()) { //do your code } } return eventConsumed; } } 
+1
source share

All Articles