94 votes

Synchroniser les positions de défilement de ScrollView - android

J'ai 2 ScrollViews dans mon layout android. Comment puis-je synchroniser leurs positions de défilement?

289voto

Andy Points 3031

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 rendu protégé et n'ont pas ajouté de crochet "setOnScrollChangedListener". Nous devrons donc le faire nous-mêmes.

Tout d'abord, nous avons 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 crochet 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 devrions spécifier cette nouvelle classe ObservableScrollView dans la mise en page, au lieu des balises ScrollView existantes.

    ...

Enfin, nous regroupons tout 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() prend en charge toutes les conditions de boucle pour nous, donc nous n'avons pas à nous inquiéter de cela. La seule réserve 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.

11voto

Taiko Points 507

Une amélioration à la solution d'Andy : Dans son code, il utilise scrollTo, le problème est que si vous faites glisser un scrollview dans une direction et ensuite faites glisser un autre dans une autre direction, vous remarquerez que le premier ne s'arrête pas dans son mouvement précédent.

Cela est dû au fait que le scrollView utilise computeScroll() pour effectuer ses gestes de lancement, et cela entre en conflit avec scrollTo.

Pour éviter cela, il suffit de programmer onScrollChanged de cette manière :

    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 boolean statique initialisé à true. (cela aide à éviter les boucles infinies sur ScrollChanged)

onOverScrolled est la seule fonction que j'ai trouvée qui pouvait être utilisée pour arrêter le scrollView de lancer (mais il pourrait y en avoir d'autres que j'ai manquées !)

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

public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}

5voto

Aaron Newton Points 41

Pourquoi ne pas simplement implémenter OnTouchListener dans votre activité. Ensuite, remplacez la méthode onTouch, puis obtenez la position de défilement du premier ScrollViewOne.getScrollY() et mettez à jour ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Juste une autre idée... :)

3voto

tungstenwang Points 191

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

Nous pouvons remplacer le noeud par dans le fichier de mise en page XML, et implémenter son NestedScrollView.OnScrollChangeListener en Java pour gérer le défilement.

Cela rend les choses plus faciles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X