2010-08-19 4 views
6

Situation: J'ai deux ScrollView à l'intérieur de deux HorizontalScrollView d'un TableRow.

Objectif: Lorsque j'appuie sur une touche de ScrollView, une autre ScrollView doit défiler autant. Par exemple, si j'ai une liste de noms sur ScrollView à gauche et les numéros de téléphone correspondants dans ScrollView à droite, le défilement d'un ScrollView ne devrait pas détruire la limite d'origine entre les noms et les numéros de téléphone.
Sync Two ScrollView

Peut-il être implémenté par onTouchEvent? Si oui, comment dois-je implémenter ceci (sur les deux ou l'un des ScrollView)?

S'il vous plaît aidez-moi sur Android Gurus!

+0

Avez-vous essayé de vous étendre ScrollView peut remplacer la méthode onTouchEvent et l'utiliser pour appeler onTouchEvent sur l'autre scrollview et transmettez-lui le même événement? – schwiz

Répondre

12

J'ai une solution simple qui fonctionne pour moi:

  • sous-classe les deux ScrollView s et passer outre à leurs événement onScrollChanged mettre à jour ScrollManager lors du défilement des changements:

    public interface ScrollNotifier { 
        public void setScrollListener(ScrollListener scrollListener); 
    
        public ScrollListener getScrollListener(); 
    } 
    
    public class SyncedScrollView extends ScrollView implements ScrollNotifier { 
    
        //... 
    
        private ScrollListener scrollListener = null; 
    
        @Override 
        protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
         super.onScrollChanged(l, t, oldl, oldt); 
         if (scrollListener != null) 
          scrollListener.onScrollChanged(this, l, t, oldl, oldt); 
        } 
        @Override 
        public void setScrollListener(ScrollListener scrollListener) { 
         this.scrollListener = scrollListener; 
        } 
        @Override 
        public ScrollListener getScrollListener() { 
         return scrollListener; 
        } 
    } 
    
  • créer une classe ScrollManager que coordonne le défilement de plusieurs participants

    public interface ScrollListener { 
        void onScrollChanged(View syncedScrollView, int l, int t, int oldl, 
         int oldt); 
    } 
    
    public class ScrollManager implements ScrollListener { 
        private static final int SCROLL_HORIZONTAL = 1; 
        private static final int SCROLL_VERTICAL = 2; 
    
        private ArrayList<ScrollNotifier> clients = new ArrayList<ScrollNotifier>(4); 
    
        private volatile boolean isSyncing = false; 
        private int scrollType = SCROLL_HORIZONTAL; 
    
        public void addScrollClient(ScrollNotifier client) { 
         clients.add(client); 
         client.setScrollListener(this); 
        } 
    
        // TODO fix dependency on all views being of equal horizontal/ vertical 
        // dimensions 
        @Override 
        public void onScrollChanged(View sender, int l, int t, int oldl, int oldt) { 
         // avoid notifications while scroll bars are being synchronized 
         if (isSyncing) { 
          return; 
         } 
    
         isSyncing = true; 
    
         // remember scroll type 
         if (l != oldl) { 
          scrollType = SCROLL_HORIZONTAL; 
         } else if (t != oldt) { 
          scrollType = SCROLL_VERTICAL; 
         } else { 
          // not sure why this should happen 
          isSyncing = false; 
          return; 
         } 
    
         // update clients 
         for (ScrollNotifier client : clients) { 
          View view = (View) client; 
          // don't update sender 
          if (view == sender) { 
           continue; 
          } 
    
          // scroll relevant views only 
          // TODO Add support for horizontal ListViews - currently weird things happen when ListView is being scrolled horizontally 
          if ((scrollType == SCROLL_HORIZONTAL && view instanceof HorizontalScrollView) 
            || (scrollType == SCROLL_VERTICAL && view instanceof ScrollView) 
            || (scrollType == SCROLL_VERTICAL && view instanceof ListView)) { 
           view.scrollTo(l, t); 
          } 
         } 
    
         isSyncing = false; 
        } 
    } 
    
  • créer la coutume ScrollView s et définir la ScrollManager pour la notification des deux

    private void setupScrolling() { 
        ScrollNotifier view; 
        ScrollManager scrollManager = new ScrollManager(); 
    
        // timeline horizontal scroller 
        view = (ScrollNotifier) findViewById(R.id.epgtimeline_container); 
        scrollManager.addScrollClient(view); 
    
        // services vertical scroller 
        view = (ScrollNotifier) findViewById(R.id.epgservices_container); 
        scrollManager.addScrollClient(view); 
    
        // content area scrollers 
        view = (ScrollNotifier) findViewById(R.id.epgevents_container_inner); 
        scrollManager.addScrollClient(view); 
        view = (ScrollNotifier) findViewById(R.id.epgevents_container_outer); 
        scrollManager.addScrollClient(view); 
    } 
    
+1

Superbe réponse, me brisait la tête depuis 2 jours maintenant en essayant d'utiliser l'onTouchEvent et de passer ce creux. J'ai finalement réussi à faire marcher ça mais ça n'a marché que dans un sens ... ça résout tous mes problèmes ... ;-) ... presque tous mes problèmes haha – stackr

1

Merci à andig pour un si grand synching scrollview solution.

Mais il y a peu de décalage entre les deux scrollviews en lançant.

Donc, j'écris ici une solution étendue pour supprimer ce décalage entre les deux scrollview.

Je viens d'utiliser la classe OverScroller et j'ai manipulé manuellement l'événement Fling dans SyncedScrollView.

Vous devez simplement remplacer SyncedScrollView par le code ci-dessous. Utilisez d'autres classes de andig's solution

import android.content.Context; 
import android.support.v4.view.ViewCompat; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.widget.OverScroller; 
import android.widget.ScrollView; 

/** 
* Created by mitul.varmora on 11/7/2016. 
* SyncedScrollView 
* https://stackoverflow.com/questions/3527119/sync-two-scrollview 
*/ 
public class SyncedScrollView extends ScrollView implements ScrollNotifier { 

    private ScrollListener scrollListener = null; 

    private OverScroller scroller; 
    private Runnable scrollerTaskRunnable; 

    public SyncedScrollView(Context context) { 
     super(context); 
     init(); 
    } 

    public SyncedScrollView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(); 
    } 

    public SyncedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(); 
    } 

    private void init() { 

     scroller = new OverScroller(getContext()); 
     scrollerTaskRunnable = new Runnable() { 
      @Override 
      public void run() { 
       scroller.computeScrollOffset(); 

       smoothScrollTo(0, scroller.getCurrY()); 

       if (!scroller.isFinished()) { 
        SyncedScrollView.this.post(this); 
       } else { 
        //deceleration ends here, do your code 
        ViewCompat.postInvalidateOnAnimation(SyncedScrollView.this); 
       } 
      } 
     }; 
    } 

    @Override 
    protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
     super.onScrollChanged(l, t, oldl, oldt); 
     if (scrollListener != null) 
      scrollListener.onScrollChanged(this, l, t, oldl, oldt); 
    } 

    @Override 
    public ScrollListener getScrollListener() { 
     return scrollListener; 
    } 

    @Override 
    public void setScrollListener(ScrollListener scrollListener) { 
     this.scrollListener = scrollListener; 
    } 

    @Override 
    public void fling(int velocityY) { 

     scroller.forceFinished(true); 
     scroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, getChildAt(0).getHeight()); 
     post(scrollerTaskRunnable); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
//  return super.onTouchEvent(ev); 
     boolean eventConsumed = super.onTouchEvent(ev); 
     if (eventConsumed && ev.getAction() == MotionEvent.ACTION_UP) { 
      if (scroller.isFinished()) { 
       //do your code 
      } 
     } 
     return eventConsumed; 
    } 
}