同步ScrollView滚动位置-android


95

我的Android布局中有2个ScrollViews。如何同步他们的滚动位置?

Answers:


289

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),谢谢您抽出宝贵的时间回答这个问题-让我感动了。再次感谢。蒂姆
蒂姆(Tim)2010年

-Andy我也是用的代码,但它是我扔的空指针厚望你能帮我
与Hemant

@hemant您能否指出抛出NullPointer的行以及您使用的是哪个Android版本?
sulai 2012年

今日英雄:救了我的命!= d
Pedrão

嗨,安迪(Andy),感谢您的代码。它的功能很棒。我想知道一件事[如果您或任何人有ans。到此]-当我拖动scrollView时,在scrollview移动时,onscrollchanged方法不会注册所有X和Y坐标。它只给我开始位置和最后位置。例如,我从Y = 10开始猛扑,然后在Y = 30离开,然后猛扑速度使它达到Y = 50,然后停止。所以只滚动了寄存器-可能是10、11、12..30,然后是49、50。我怎样才能使其也注册所有中间位置并获得从10到50的所有坐标?
炼金术士2013年

11

安迪解决方案的一个改进:在他的代码中,他使用了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);
}

2
儿子,真是太好了!
FranciscoBouza

5

为什么不只是OnTouchListener在您的活动中实施。然后覆盖onTouch方法,然后获取第一个的滚动位置 ScrollViewOne.getScrollY()并更新ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

只是另一个想法... :)


7
这可以工作,但是当您触摸并放开时,滚动视图可以在放开后滚动更多,但无法捕获。
richy 2012年

在这种情况下,如何在Java中引用/获取scrollView ScrollViewOne的引用?
兔子兔子2012年

还有其他滚动方式(例如onTrackballEvent())
surfealokesea 2013年

这是一个非常好的主意,但是与其获取并设置滚动y位置,不如将touch事件调度到第二个scrollview会更容易。这还将使“文件”在第二个滚动视图中进行。MotionEvent newEvent = MotionEvent.obtain(event); documentsScrollView.dispatchTouchEvent(newEvent);
Eduard Mossinkoff 2014年

3

在Android support-v4程序包中,Android提供了一个名为的新类NestedScrollView

我们可以在xml布局中将其替换为,并在Java中实现该<ScrollView>节点以处理滚动。<android.support.v4.widget.NestedScrollView>NestedScrollView.OnScrollChangeListener

这使事情变得容易。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.