ScrollViewのスクロール位置を同期する-Android


95

Androidレイアウトに2つのScrollViewがあります。スクロール位置を同期するにはどうすればよいですか?

回答:


289

ScrollViewにはメソッドがあります...

protected void onScrollChanged(int x, int y, int oldx, int oldy)

残念ながら、Googleはそれにアクセスする必要があるとは決して思っていませんでした。だから私たちは自分たちでそれをしなければならないでしょう。

まず、インターフェースが必要です。

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);
        }
    }

}

また、既存のScrollViewタグの代わりに、レイアウトにこの新しいObservableScrollViewクラスを指定する必要があります。

<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この回答をお寄せいただきありがとうございます。再度、感謝します。ティム
ティム

-Andy私もコードを使用していますが、それは私にnullポインタの期待を投げています
急いで

@hemant NullPointerがスローされた行と、使用しているAndroidのバージョンを指摘していただけますか?
sulai

今日のヒーロー:私の命を救った!= D
Pedrão

こんにちはアンディ、コードに感謝します。それは素晴らしい働きをします。私は1つのことを知りたいです(あなたまたはany1がansを持っている場合)。これに]-scrollViewをフリングすると、scrollviewが移動している間、onscrollchangedメソッドはすべてのX座標とY座標を登録しません。それは私に開始位置と最終位置を与えるだけです。たとえば、Y = 10からフリングを開始し、Y = 30を離れると、フリング速度がY = 50になり、停止します。したがって、onscrollchangedはレジスタのみ(おそらく10、11、12..30、次に49、50)のみを変更しました。どのようにしてすべての中間位置も登録し、10から50までのすべての座標を取得できますか?
錬金術師

11

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);
}

2
なんて素敵なキャッチ、息子!
FranciscoBouza

5

なぜOnTouchListenerあなたの活動に単に実装しないのですか?次に、onTouchメソッドをオーバーライドし、最初のスクロール位置を取得して ScrollViewOne.getScrollY()更新しますScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

ちょうど別のアイデア... :)


7
これは機能しますが、タッチして離すと、キャプチャされていない離した後、スクロールビューがもう少しスクロールできます。
リッチな2012

この場合、JavaでscrollView ScrollViewOneの参照をどのように参照/取得できますか?
バニーウサギ

スクロールする他の方法もあります(onTrackballEvent()など)
surfealokesea

非常に良いアイデアですが、スクロールのy位置を取得して設定する代わりに、タッチイベントを2番目のスクロールビューにディスパッチするだけの方が簡単です。これにより、2番目のスクロールビューでも「フリング」が続行されます。MotionEvent newEvent = MotionEvent.obtain(event); documentsScrollView.dispatchTouchEvent(newEvent);
Eduard Mossinkoff 2014年

3

Android support-v4パッケージで、Androidはという名前の新しいクラスを提供しNestedScrollViewます。

レイアウトxmlで<ScrollView>ノードを 置き換えることができ、スクロールを処理するためにJavaでノードを<android.support.v4.widget.NestedScrollView>実装NestedScrollView.OnScrollChangeListenerします。

それは物事を簡単にします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.