2010-08-19 100 views
6

情況: 我有一個TableRow的兩個Horizo​​ntalScrollView的每個裏面有兩個ScrollView。

目標: 當我觸摸拖動ScrollView之一時,另一個ScrollView必須滾動儘可能多。例如,如果我在左邊的ScrollView上有一個名稱列表,並在右側的ScrollView中有相應的電話號碼,那麼滾動一個ScrollView不應該破壞名稱和電話號碼之間的原始邊界。
同步兩個ScrollView

它可以通過onTouchEvent實現嗎?如果是這樣,我應該如何實現這個(在ScrollView的兩個或其中之一)?

請幫我拿出Android大師!

+0

其他類你試圖滾動型延伸到可以覆蓋的onTouchEvent方法,並用它來調用您的其他滾動型的onTouchEvent,並將它傳遞相同的事件? – schwiz 2010-11-02 21:52:40

回答

12

我有一個簡單的解決方案,爲我的作品:

  • 子類都ScrollView S和覆蓋其onScrollChanged事件來更新ScrollManager滾動變化時:

    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; 
        } 
    } 
    
  • 創建一個ScrollManager類協調多個參與者的滾動

    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; 
        } 
    } 
    
  • 創建自定義ScrollView S和兩個

    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

非常棒的回答,在這段時間裏我已經打破了2天,試圖使用onTouchEvent並通過這個槽。我終於得到了這個工作,但它只能用一種方法......這解決了我所有的問題...... ;-) ......幾乎所有的問題都是哈哈 – stackr 2015-03-04 10:18:31

1

由於設置ScrollManager的通知andig對於這樣一個偉大的synching scrollview solution

但是在兩個滾動瀏覽器之間有一點點滯後。

所以我寫這裏擴展的解決方案,以消除這兩個scrollview之間的滯後。

我剛剛使用了OverScroller類,並在SyncedScrollView中手動處理了拋擲事件。

您必須用下面的代碼替換SyncedScrollView。 使用來自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; 
    } 
}