Synchroniser les positions de défilement ScrollView - Android

95

J'ai 2 ScrollViews dans ma mise en page Android. Comment puis-je synchroniser leurs positions de défilement?

Tim
la source

Réponses:

289

Il existe une méthode dans ScrollView ...

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

Malheureusement, Google n'a jamais pensé que nous aurions besoin d'y accéder, c'est pourquoi ils l'ont protégé et n'ont pas ajouté de hook "setOnScrollChangedListener". Nous devrons donc le faire pour nous-mêmes.

Nous avons d'abord besoin d'une interface.

package com.test;

public interface ScrollViewListener {

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

}

Ensuite, nous devons remplacer la classe ScrollView, pour fournir le hook 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);
        }
    }

}

Et nous devons spécifier cette nouvelle classe ObservableScrollView dans la mise en page, au lieu des balises ScrollView existantes.

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

    ...

</com.test.ObservableScrollView>

Enfin, nous mettons tout cela ensemble dans la classe 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);
        }
    }

}

Le code scrollTo () s'occupe de toutes les conditions de boucle pour nous, nous n'avons donc pas à nous en soucier. La seule mise en garde est que cette solution n'est pas garantie de fonctionner dans les futures versions d'Android, car nous remplaçons une méthode protégée.

Andy
la source
Salut Andy Merci d'avoir pris le temps de mettre cette réponse - m'a fait avancer. Merci encore. Tim
Tim
-Et j'utilise aussi le code mais il me lance une attente de pointeur nul pouvez-vous m'aider
Hemant
@hemant Pouvez-vous indiquer dans quelle ligne le NullPointer est lancé et avec quelle version d'Android vous travaillez?
sulai
Héros du jour: m'a sauvé la vie! = D
Pedrão
Salut Andy, merci pour le code. Il fonctionne très bien. J'aimerais savoir une chose [si vous ou n'importe qui1 avez ans. to this] - Lorsque je lance mon scrollView, la méthode onscrollchanged n'enregistre pas toutes les coordonnées X et Y pendant que la scrollview se déplace. Cela ne me donne que la position de départ et la dernière position. Disons par exemple que je commence le fling à partir de Y = 10 et que je pars à Y = 30, puis que la vitesse du fling l'amène à Y = 50 puis s'arrête. Donc onscrollchanged seulement registres-peut-être 10, 11, 12..30 et puis 49, 50.Comment puis-je faire enregistrer tous les emplacements intermédiaires et obtenir toutes les coordonnées de 10 à 50 ??
alchimiste le
11

Une amélioration de la solution d'Andy: dans son code, il utilise scrollTo, le problème est que si vous lancez une vue de défilement dans une direction puis en lancez une autre dans une autre direction, vous remarquerez que la première n'arrête pas sa précédente aventure. mouvement.

Cela est dû au fait que scrollView utilise computeScroll () pour faire ses mouvements de projection et qu'il entre en conflit avec scrollTo.

Afin d'éviter cela, programmez simplement onScrollChanged de cette façon:

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

avec interceptScroll un booléen statique initialisé à true. (cela permet d'éviter les boucles infinies sur ScrollChanged)

onOverScrolled est la seule fonction que j'ai trouvée qui pourrait être utilisée pour empêcher le scrollView de flinguer (mais il y en a peut-être d'autres que j'ai manquées!)

Pour accéder à cette fonction (qui est protégée), vous devez l'ajouter à votre ObservableScrollViewer

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
Taiko
la source
2
Quelle belle prise, fils!
FranciscoBouza
5

Pourquoi pas simplement des implémentations OnTouchListenerdans votre activité. Remplacez ensuite la méthode onTouch, puis récupérez la position de défilement du premier ScrollViewOne.getScrollY()et mettez à jourScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Juste une autre idée ... :)

Aaron Newton
la source
7
Cela peut fonctionner, mais lorsque vous touchez et relâchez la vue de défilement peut défiler un peu plus après avoir relâché ce qui n'est pas capturé.
richy
Comment puis-je référencer / obtenir la référence du scrollView ScrollViewOne dans ce cas en java?
Bunny Rabbit
Il existe également d'autres moyens de faire défiler (onTrackballEvent () par exemple)
surfealokesea
C'est une très bonne idée, mais au lieu d'obtenir et de définir la position y du défilement, il est plus facile de simplement envoyer l'événement tactile à la deuxième vue de défilement. Cela maintiendra également le "fling" dans la deuxième vue de défilement. MotionEvent newEvent = MotionEvent.obtain (événement); documentsScrollView.dispatchTouchEvent (newEvent);
Eduard Mossinkoff
3

Dans le package Android support-v4, Android fournit une nouvelle classe nommée NestedScrollView.

nous pouvons remplacer le <ScrollView>nœud par <android.support.v4.widget.NestedScrollView>dans la mise en page xml, et implémenter son NestedScrollView.OnScrollChangeListeneren Java pour gérer le défilement.

Cela facilite les choses.

Wangzhangjian
la source