情况:
我在TableRow的两个HorizontalScrollView中分别有两个ScrollView
的目标:
当我触摸拖动其中一个ScrollView时,另一个ScrollView必须滚动尽可能多。例如,如果我在左侧ScrollView上有一个名称列表,并且右侧ScrollView中有相应的电话号码,则滚动一个ScrollView不应该破坏名称和电话号码之间的原始界限。
可以通过onTouchEvent实现吗?如果是这样,我应该如何实现它(在两个或一个ScrollView上)?
请帮我解决Android Gurus!
答案 0 :(得分:16)
我有一个适合我的简单解决方案:
将两个ScrollView
子类化,并在滚动更改时覆盖其onScrollChanged
事件以更新ScrollManager
:
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;
}
}
创建一个ScrollManager
类,用于协调多个参与者的滚动
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;
}
}
创建自定义ScrollView
并在{/ 1>上设置ScrollManager
通知
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);
}
答案 1 :(得分:2)
感谢andig这么棒的synching scrollview solution。
但是在投掷时两个滚动视图之间存在一点延迟。
所以我在这里写扩展解决方案,以消除两个scrollview之间的延迟。
我刚刚使用过OverScroller类并在SyncedScrollView中手动处理了fling事件。
您必须使用以下代码替换SyncedScrollView。 使用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;
}
}