Sync Two ScrollView
Situation: I have two ScrollView inside of each of two HorizontalScrollView of a TableRow.
Goal: When I touch drag one of ScrollView, another ScrollView must scroll as much. For instance, if I have a list of name on left ScrollView and corres开发者_如何学Cponding phone numbers in right ScrollView, scrolling one ScrollView should not destroy the original bounding between the names and phone numbers.Can it be implemented by onTouchEvent? If so, how should I implement this(on both or one of the ScrollView)?
Please help me out Android Gurus!
I have a simple solution that works for me:
subclass both
ScrollView
s and override theironScrollChanged
event to updateScrollManager
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 participantspublic 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 the custom
ScrollView
s and set theScrollManager
for notification on bothprivate 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); }
Thanks to andig for such a great synching scrollview solution.
But there is little bit lag between both scrollviews while flinging.
So i am writing here extended solution to remove that lag between both scrollview.
I have just used OverScroller class and handled fling event manually in SyncedScrollView.
You have to just replace SyncedScrollView with below code. Use other classes from andig's 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;
}
}
精彩评论