web-dev-qa-db-fra.com

Synchroniser les positions de défilement ScrollView - android

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

94
Tim

Il y a 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 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 disposition, au lieu des balises ScrollView existantes.

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

    ...

</com.test.ObservableScrollView>

Enfin, nous avons tout rassemblé 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, nous n'avons donc pas à nous en préoccuper. 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.

289
Andy

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 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 effectuer ses gestes et qu'il entre en conflit avec scrollTo.

Pour é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 voler (mais il y en a peut-être d'autres que j'ai ratés!)

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

Pourquoi ne pas simplement implémenter OnTouchListener dans votre activité. Remplacez ensuite la méthode onTouch, puis obtenez la position de défilement de la première ScrollViewOne.getScrollY() et mettez à jour ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

Juste une autre idée ... :)

5
Aaron Newton

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

nous pouvons remplacer le <ScrollView> noeud avec <Android.support.v4.widget.NestedScrollView> dans la mise en page xml, et implémente son NestedScrollView.OnScrollChangeListener in Java pour gérer le défilement.

Cela rend les choses plus faciles.

3
wangzhangjian