【发布时间】:2011-04-26 07:46:11
【问题描述】:
我的 android 布局中有 2 个 ScrollView。如何同步他们的滚动位置?
【问题讨论】:
我的 android 布局中有 2 个 ScrollView。如何同步他们的滚动位置?
【问题讨论】:
ScrollView中有一个方法...
protected void onScrollChanged(int x, int y, int oldx, int oldy)
不幸的是,Google 从未想过我们需要访问它,这就是为什么他们将其设为受保护并且没有添加“setOnScrollChangedListener”挂钩的原因。所以我们必须自己做。
首先我们需要一个接口。
package com.test;
public interface ScrollViewListener {
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
然后我们需要重写 ScrollView 类,提供 ScrollViewListener 钩子。
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);
}
}
}
我们应该在布局中指定这个新的 ObservableScrollView 类,而不是现有的 ScrollView 标签。
<com.test.ObservableScrollView
android:id="@+id/scrollview1"
... >
...
</com.test.ObservableScrollView>
最后,我们将它们放在 Layout 类中。
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);
}
}
}
scrollTo() 代码为我们处理任何循环条件,因此我们无需担心。唯一需要注意的是,这个解决方案不能保证在未来的 Android 版本中有效,因为我们覆盖了一个受保护的方法。
【讨论】:
对 Andy 解决方案的改进: 在他的代码中,他使用了scrollTo,问题是,如果您将一个滚动视图向一个方向移动,然后将另一个滚动视图向另一个方向移动,您会注意到第一个并没有停止他之前的移动。
这是因为scrollView使用computeScroll()来做它的投掷手势,并且它进入与scrollTo冲突。
为了防止这种情况,只需以这种方式对 onScrollChanged 进行编程:
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;
}
}
使用interceptScroll 初始化为true 的静态布尔值。 (这有助于避免 ScrollChanged 出现无限循环)
onOverScrolled 是我发现的唯一一个可以用来阻止 scrollView 的函数(但可能还有其他我错过了!)
为了访问此功能(受保护),您必须将其添加到您的 ObservableScrollViewer
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
【讨论】:
为什么不在你的活动中实现OnTouchListener。
然后重写onTouch方法,然后获取第一个ScrollViewOne.getScrollY()的滚动位置并更新ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());
只是另一个想法... :)
【讨论】:
在 Android support-v4 包中,Android 提供了一个名为 NestedScrollView 的新类。
我们可以在布局xml中用<android.support.v4.widget.NestedScrollView>替换<ScrollView>节点,
并在 Java 中实现其NestedScrollView.OnScrollChangeListener 来处理滚动。
这让事情变得更容易。
【讨论】: