ScrollView-Bildlaufpositionen synchronisieren - android

95

Ich habe 2 ScrollViews in meinem Android-Layout. Wie kann ich ihre Bildlaufpositionen synchronisieren?

Tim
quelle

Antworten:

289

Es gibt eine Methode in ScrollView ...

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

Leider hätte Google nie gedacht, dass wir darauf zugreifen müssen, weshalb sie es geschützt haben und keinen "setOnScrollChangedListener" -Hook hinzugefügt haben. Also müssen wir das für uns selbst tun.

Zuerst brauchen wir eine Schnittstelle.

package com.test;

public interface ScrollViewListener {

    void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);

}

Dann müssen wir die ScrollView-Klasse überschreiben, um den ScrollViewListener-Hook bereitzustellen.

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

}

Und wir sollten diese neue ObservableScrollView-Klasse im Layout anstelle der vorhandenen ScrollView-Tags angeben.

<com.test.ObservableScrollView
    android:id="@+id/scrollview1"
    ... >

    ...

</com.test.ObservableScrollView>

Schließlich haben wir alles in der Layout-Klasse zusammengefasst.

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

}

Der scrollTo () - Code kümmert sich um alle Schleifenbedingungen für uns, sodass wir uns darüber keine Sorgen machen müssen. Die einzige Einschränkung ist, dass diese Lösung in zukünftigen Android-Versionen nicht garantiert funktioniert, da wir eine geschützte Methode überschreiben.

Andy
quelle
Hallo Andy, danke, dass du dir die Zeit genommen hast, diese Antwort zu geben - hat mich vorwärts gebracht. Danke noch einmal. Tim
Tim
-Andy Ich benutze auch den Code, aber es wirft mir Null Zeiger Erwartung können Sie mir helfen
Hemant
@hemant Kannst du bitte darauf hinweisen, in welche Zeile der NullPointer geworfen wird und mit welcher Android-Version du arbeitest?
Sulai
Held des Tages: Rettete mein Leben! = D
Pedrão
Hallo Andy, danke für den Code. Es funktioniert großartig. Ich würde gerne eine Sache wissen [wenn du oder jemand1 ans hat. dazu] - Wenn ich meine scrollView schleudere, registriert die onscrollchanged-Methode nicht alle X- und Y-Koordinaten, während sich die scrollview bewegt. Es gibt mir nur die Startposition und die letzte Position. Sagen wir zum Beispiel: Ich starte den Schleudergang von Y = 10 und lasse ihn bei Y = 30 und dann bringt ihn die Schleudergeschwindigkeit auf Y = 50 und stoppt dann. Also wurden nur die Register geändert - vielleicht 10, 11, 12..30 und dann 49, 50. Wie kann ich dafür sorgen, dass auch alle Zwischenorte registriert werden und alle Koordinaten von 10 bis 50 erhalten werden?
Alchemist
11

Eine Verbesserung von Andys Lösung: In seinem Code verwendet er scrollTo. Das Problem ist, wenn Sie eine Bildlaufansicht in eine Richtung und dann eine andere in eine andere Richtung werfen, werden Sie feststellen, dass die erste seine vorherige Bewegung nicht aufhält Bewegung.

Dies liegt an der Tatsache, dass scrollView computeScroll () verwendet, um seine schleudernden Gesten auszuführen, und dass es in Konflikt mit scrollTo gerät.

Um dies zu verhindern, programmieren Sie den onScrollChanged einfach folgendermaßen:

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

Mit interceptScroll wird ein statischer Boolescher Wert auf true initialisiert. (Dies hilft, Endlosschleifen bei ScrollChanged zu vermeiden.)

onOverScrolled ist die einzige Funktion, die ich gefunden habe, um das Scrollen der scrollView zu verhindern (aber es gibt möglicherweise andere, die ich verpasst habe!).

Um auf diese Funktion (die geschützt ist) zuzugreifen, müssen Sie diese Ihrem ObservableScrollViewer hinzufügen

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Taiko
quelle
2
Was für ein schöner Fang, mein Sohn!
FranciscoBouza
5

Warum nicht einfach OnTouchListenerin Ihre Aktivität implementieren . Überschreiben Sie dann die onTouch-Methode, rufen Sie die Bildlaufposition der ersten ab ScrollViewOne.getScrollY()und aktualisieren Sie sieScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Nur eine andere Idee ... :)

Aaron Newton
quelle
7
Dies kann funktionieren, aber wenn Sie berühren und loslassen, kann die Bildlaufansicht nach dem Loslassen etwas mehr scrollen, was nicht erfasst wird.
Richy
Wie kann ich die Aktualisierung der scrollView ScrollViewOne in diesem Fall in Java referenzieren / erhalten?
Bunny Rabbit
Es gibt auch andere Möglichkeiten zum Scrollen (zum Beispiel onTrackballEvent ())
surfealokesea
Es ist eine sehr gute Idee, aber anstatt die Position des Bildlaufs zu erhalten und einzustellen, ist es einfacher, das Berührungsereignis einfach an die zweite Bildlaufansicht zu senden. Dies hält auch das "Schleudern" in der zweiten Bildlaufansicht aufrecht. MotionEvent newEvent = MotionEvent.obtain (Ereignis); documentsScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff
3

Im Android Support-v4-Paket bietet Android eine neue Klasse mit dem Namen NestedScrollView.

Wir können den <ScrollView>Knoten durch <android.support.v4.widget.NestedScrollView>in Layout-XML ersetzen und ihn NestedScrollView.OnScrollChangeListenerin Java implementieren , um das Scrollen zu handhaben.

Das macht die Sache einfacher.

Wangzhangjian
quelle