Synchroniser les positions de défilement ScrollView – Android

J’ai 2 ScrollViews dans ma présentation Android. Comment synchroniser leurs positions de défilement?

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 devions y accéder, c’est pourquoi ils l’ont protégée 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.AtsortingbuteSet; import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private ScrollViewListener scrollViewListener = null; public ObservableScrollView(Context context) { super(context); } public ObservableScrollView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } public ObservableScrollView(Context context, AtsortingbuteSet 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 présentation, au lieu des balises ScrollView existantes.

  ...  

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 () prend en charge toutes les conditions de boucle, nous n’avons donc pas à nous en préoccuper. Le seul inconvénient est que cette solution n’est pas garantie pour fonctionner dans les futures versions d’Android, car nous remplaçons une méthode protégée.

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

Cela est dû au fait que scrollView utilise computeScroll () pour faire ses gestes, et il entre en conflit avec scrollTo.

Pour éviter cela, programmez simplement le 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 évite les boucles infinies sur ScrollChanged)

onOverScrolled est la seule fonction que j’ai trouvée qui pourrait être utilisée pour empêcher le défilement de scrollView (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 l’append à votre ObservableScrollViewer

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

Pourquoi ne pas simplement implémenter OnTouchListener dans votre activité. Remplacez ensuite 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 … 🙂

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

nous pouvons remplacer le nœud par dans layout XML et implémenter son NestedScrollView.OnScrollChangeListener en Java pour gérer le défilement.

Cela facilite les choses.