2013-04-19 34 views
12

Tôi đang cố gắng để đồng bộ hóa hai ViewPager s, như dường như có khá nhiều người trước tôi, và tôi đã có như xa như thế này:Đồng bộ hóa hai ViewPagers sử dụng OnPageChangeListener

private ViewPager mNavPager; 

private ViewPager mMainPager; 

private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() { 

    private boolean mNavDragging; 
    private int mScrollPosition; 

    @Override 
    public void onPageSelected(int position) { 
     mScrollPosition = position; 
    } 

    @Override 
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
     if(mNavDragging) 
      mMainPager.scrollTo(positionOffsetPixels, 0); 
    } 

    @Override 
    public void onPageScrollStateChanged(int state) { 
     switch(state) { 
     case ViewPager.SCROLL_STATE_DRAGGING: 
     case ViewPager.SCROLL_STATE_SETTLING: 
      mNavDragging = true; 
      break; 
     case ViewPager.SCROLL_STATE_IDLE: 
      mNavDragging = false; 
      break; 
     } 
    } 
}; 

private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() { 

    private boolean mMainDragging; 
    private int mScrollPosition; 

    @Override 
    public void onPageSelected(int position) { 
     mScrollPosition = position; 
    } 

    @Override 
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
     if(mMainDragging) 
      mNavPager.scrollTo(positionOffsetPixels, 0); 
    } 

    @Override 
    public void onPageScrollStateChanged(int state) { 
     switch(state) { 
     case ViewPager.SCROLL_STATE_DRAGGING: 
     case ViewPager.SCROLL_STATE_SETTLING: 
      mMainDragging = true; 
      break; 
     case ViewPager.SCROLL_STATE_IDLE: 
      mMainDragging = false; 
      break; 
     } 
    } 
}; 

Nếu một trong hai là được cuộn theo cách thủ công, mặt còn lại được gạch vào nó bằng thuộc tính trạng thái cuộn. Nó hoạt động đẹp cho đến khi các vật phẩm đạt đến vị trí cuối cùng của chúng; tại thời điểm này, máy nhắn tin được vuốt ngay lập tức quay lại mục đã chọn trước đó, như thể việc di chuyển đã không diễn ra.

Tôi đã thử gọi ViewPager#setCurrentItem(mScrolledPosition) với một loạt các ràng buộc logic khác nhau nhưng điều đó không hoạt động, mặc dù đôi khi nó làm cho nó tồi tệ hơn. Tôi cảm thấy như thể phải có một cái gì đó có thể được thực hiện với điều đó nhưng tôi không chắc chắn những gì.

Làm cách nào để tôi có được máy nhắn tin được giữ ở đúng vị trí?

Trả lời

2

Điều này làm mọi thứ đúng ngoại trừ đôi khi bỏ lỡ các cảnh rất nhanh trên chế độ xem được xem. Đối với một số lý do bao gồm các sự kiện kéo giả trong giai đoạn giải quyết gây ra vấn đề thực sự, mặc dù.

private ViewPager mNavPager; 

private ViewPager mMainPager; 

private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() { 

    private int mLastScrollPosition; 
    private int mLastPagePosition; 

    @Override 
    public void onPageSelected(int position) { 
     mLastPagePosition = position; 
    } 

    @Override 
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
     if(mMainPager.isFakeDragging()) { 
      int absoluteOffsetPixels = positionOffsetPixels; 
      if(mLastPagePosition!=position) { 
       absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth(); 
       mLastPagePosition = position; 
      } 
      Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels)); 
      mMainPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels); 
      mLastScrollPosition = positionOffsetPixels; 
     } 
    } 

    @Override 
    public void onPageScrollStateChanged(int state) { 
     if(!mNavPager.isFakeDragging()) { 
      switch(state) { 
      case ViewPager.SCROLL_STATE_DRAGGING: 
       if(!mMainPager.isFakeDragging()) 
        mMainPager.beginFakeDrag(); 
       break; 
      case ViewPager.SCROLL_STATE_SETTLING: 
      case ViewPager.SCROLL_STATE_IDLE: 
       if(mMainPager.isFakeDragging()) { 
        mMainPager.endFakeDrag(); 
        mLastScrollPosition = 0; 
       } 
       break; 
      } 
     } 
    } 
}; 

private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() { 

    private int mLastScrollPosition; 
    private int mLastPagePosition; 

    @Override 
    public void onPageSelected(int position) { 
     mLastPagePosition = position; 
    } 

    @Override 
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
     if(mNavPager.isFakeDragging()) { 
      int absoluteOffsetPixels = positionOffsetPixels; 
      if(mLastPagePosition!=position) { 
       absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth(); 
       mLastPagePosition = position; 
      } 
      Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels)); 
      mNavPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels); 
      mLastScrollPosition = positionOffsetPixels; 
     } 
    } 

    @Override 
    public void onPageScrollStateChanged(int state) { 
     if(!mMainPager.isFakeDragging()) { 
      switch(state) { 
      case ViewPager.SCROLL_STATE_DRAGGING: 
       if(!mNavPager.isFakeDragging()) 
        mNavPager.beginFakeDrag(); 
       break; 
      case ViewPager.SCROLL_STATE_SETTLING: 
      case ViewPager.SCROLL_STATE_IDLE: 
       if(mNavPager.isFakeDragging()) { 
        mNavPager.endFakeDrag(); 
        mLastScrollPosition = 0; 
       } 
       break; 
      } 
     } 
    } 
}; 

EDIT

bây giờ tôi tin rằng điều này là không thể nếu không có mã tùy chỉnh khá đáng kể. Lý do cơ bản là cả hai ViewPager s có một bên trong VelocityTracker, điều khiển cuộn. Kể từ khi MotionEvent s được chuyển vào có thể không được chuyển đến VelocityTracker tại cùng thời điểm tương đối cho mỗi máy nhắn tin, trình theo dõi đôi khi sẽ đạt được các kết luận khác nhau về cách phản ứng.

Tuy nhiên, nó thể sử dụng một biến đổi PagerTitleStrip để có được theo dõi chính xác của một ViewPager, và chuyển các sự kiện liên lạc bị bắt bởi các dải trực tiếp đến ViewPager.

The source for PagerTitleStrip is here.

chung, những gì cần phải được thực hiện để làm cho công việc này là như sau: thay thế mPrevText, mCurrTextmNextText với quan điểm của các loại mà bạn muốn sử dụng; xóa các hàm onAttachedToWindow()onDetachedFromWindow(); loại bỏ các cuộc gọi đến số PagerAdapter đối phó với người quan sát bộ dữ liệu và thêm OnTouchListener vào dải đã sửa đổi mà giả sẽ kéo máy nhắn tin chính. Bạn cũng cần phải thêm dải tiêu đề đã sửa đổi làm OnPageChangeListener vào số ViewPager vì trình nghe nội bộ không hiển thị bên ngoài gói.

Đây có phải là nỗi đau khổng lồ không? Vâng. Nhưng nó đã có tác dụng. Tôi sẽ sớm viết chi tiết hơn.

+0

Bạn sẽ viết nó một cách chi tiết hơn? –

5

Tôi đã giải quyết được sự cố mà không sử dụng Trình nghe. Vì vậy, bạn có thể sử dụng trình nghe cho một số nội dung khác và sẽ làm cho mã trông sạch hơn.

Tôi biết câu hỏi của nó được hỏi từ lâu. Tôi đã tìm kiếm một giải pháp và tự giải quyết nó.

Đây là cách mã Tuỳ chỉnh ViewPager của tôi là

public class CustomPager extends ViewPager { 
CustomPager mCustomPager; 
private boolean forSuper; 

public CustomPager(Context context) { 
    super(context); 
} 

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

@Override 
public boolean onInterceptTouchEvent(MotionEvent arg0) { 
    if (!forSuper) { 

     mCustomPager.forSuper(true); 
     mCustomPager.onInterceptTouchEvent(arg0); 
     mCustomPager.forSuper(false); 
    } 
    return super.onInterceptTouchEvent(arg0); 
} 

@Override 
public boolean onTouchEvent(MotionEvent arg0) { 
    if (!forSuper) { 
     mCustomPager.forSuper(true); 
     mCustomPager.onTouchEvent(arg0); 
     mCustomPager.forSuper(false); 
    } 
    return super.onTouchEvent(arg0); 
} 

public void setViewPager(CustomPager customPager) { 
    mCustomPager = customPager; 
} 

public void forSuper(boolean forSuper) { 
    this.forSuper = forSuper; 
} 

@Override 
public void setCurrentItem(int item, boolean smoothScroll) { 
    if (!forSuper) { 
     mCustomPager.forSuper(true); 
     mCustomPager.setCurrentItem(item, smoothScroll); 
     mCustomPager.forSuper(false); 
    } 
    super.setCurrentItem(item, smoothScroll); 
} 

@Override 
public void setCurrentItem(int item) { 
    if (!forSuper) { 
     mCustomPager.forSuper(true); 
     mCustomPager.setCurrentItem(item); 
     mCustomPager.forSuper(false); 
    } 
    super.setCurrentItem(item); 

} 

} 

Và bạn phải thiết lập máy nhắn tin như thế này trước khi bộ chuyển đổi được thiết lập và ngay sau khi nhận được tài liệu tham khảo bằng cách sử dụng ID.

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    backPager=(CustomPager) findViewById(R.id.BackPager); 
    frontPager = (CustomPager) findViewById(R.id.frontPager); 
    frontPager.setViewPager(backPager); 
    backPager.setViewPager(frontPager); 
    backPager.setAdapter(your Adapter); 
    frontPager.setAdapter(your Adapter); 
} 

Phải đặt ViewPagers trước khi chỉ định bộ điều hợp.

+0

Điều này trông giống như một cái gì đó tôi đã cố gắng, tôi sẽ có một đi với điều này, cảm ơn bạn! –

+0

Tôi đang sử dụng nó trong ứng dụng của mình. Bạn có suy nghĩ gì về điều đó. Sản phẩm có hoạt động chính xác cho bạn không? –

+0

@Andrew Wyld Tôi đang cố gắng để đạt được cùng một điều viewpager. Có thành công nào trong việc này không? – user2095470

12

tôi giải quyết vấn đề này trong một dễ dàng hơn nhiều (và sạch hơn) cách sử dụng OnPageChangeListener:

mViewPager1.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { 

     private int mScrollState = ViewPager.SCROLL_STATE_IDLE; 

     @Override 
     public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { 
     if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { 
      return; 
     } 
     mViewPager2.scrollTo(mViewPager1.getScrollX(), mViewPager2.getScrollY()); 
     } 

     @Override 
     public void onPageSelected(final int position) { 

     } 

     @Override 
     public void onPageScrollStateChanged(final int state) { 
     mScrollState = state; 
     if (state == ViewPager.SCROLL_STATE_IDLE) { 
      mViewPager2.setCurrentItem(mViewPager1.getCurrentItem(), false); 
     } 
     } 
}); 

mViewPager2.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { 

     private int mScrollState = ViewPager.SCROLL_STATE_IDLE; 

     @Override 
     public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { 
     if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { 
      return; 
     } 
     mViewPager1.scrollTo(mViewPager2.getScrollX(), mViewPager1.getScrollY()); 
     } 

     @Override 
     public void onPageSelected(final int position) { 

     } 

     @Override 
     public void onPageScrollStateChanged(final int state) { 
     mScrollState = state; 
     if (state == ViewPager.SCROLL_STATE_IDLE) { 
      mViewPager1.setCurrentItem(mViewPager2.getCurrentItem(), false); 
     } 
     } 
}); 
+0

Tôi đã thử một cái gì đó như thế này và thấy nó chỉ làm việc một phần: sự khác biệt nhỏ trong thời gian gây ra vận tốc của máy nhắn tin slaved để vượt qua hoặc undershoot đôi khi. –

+0

Cho đến nay tôi chưa bao giờ gặp vấn đề gì. Mọi thứ hoạt động trơn tru. –

+0

Bạn nên thay đổi mục hiện tại trên 'onPageScrollStateChanged()' khi 'state' là' SCROLL_STATE_SETTLING'. Đó là khi Android làm điều đó. –

0

Tất cả các câu trả lời là khoảng currect trong một số tình huống. Ở đây tôi đưa ra một câu trả lời hơn mà sẽ sử dụng để trượt cả ViewPagers đồng thời liệu có kích thước là cùng hoặc không:

viewPagerBanner.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 
    private int scrollState = ViewPager.SCROLL_STATE_IDLE; 
    // Indicates that the pager is in an idle, settled state. 
    // The current page is fully in view and no animation is in progress. 

    @Override 
    public void onPageScrolled(int position, float positionOffset, 
           int positionOffsetPixels) { 
     if (scrollState == ViewPager.SCROLL_STATE_IDLE) { 
      return; 
     } 
     viewPagerTitle.scrollTo(viewPagerBanner.getScrollX()* 
           viewPagerTitle.getWidth()/ 
           viewPagerBanner.getWidth(), 0); 
     // We are not interested in Y axis position 
    } 

    @Override 
    public void onPageSelected(int position) {} 

    @Override 
    public void onPageScrollStateChanged(int state) { 
     scrollState = state; 
     if (state == ViewPager.SCROLL_STATE_IDLE) { 
      viewPagerTitle.setCurrentItem(viewPagerBanner.getCurrentItem(), false); 
     } 
    } 
}); 
1

Với sự giúp đỡ từ tất cả các câu trả lời khác tôi đã tạo ra một lớp nên nó có thể dễ dàng được thực hiện.

public class OnPageChangeListernerSync implements ViewPager.OnPageChangeListener { 

    private ViewPager master; 
    private ViewPager slave; 
    private int mScrollState = ViewPager.SCROLL_STATE_IDLE; 

    public OnPageChangeListernerSync(ViewPager master, ViewPager slave){ 
     this.master = master; 
     this.slave = slave; 
    } 

    @Override 
    public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { 
     if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { 
      return; 
     } 
     this.slave.scrollTo(this.master.getScrollX()* 
       this.slave.getWidth()/ 
       this.master.getWidth(), 0); 
    } 

    @Override 
    public void onPageSelected(final int position) { 

    } 

    @Override 
    public void onPageScrollStateChanged(final int state) { 
     mScrollState = state; 
     if (state == ViewPager.SCROLL_STATE_IDLE) { 
      this.slave.setCurrentItem(this.master 
        .getCurrentItem(), false); 
     } 
    } 
} 

...

// In your activity 
this.upperPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.upperPager, this.lowerPager)); 

this.lowerPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.lowerPager, this.upperPager)); 
1

Tôi đã có một vấn đề tương tự, cũng cần phải kết hợp đồng bộ của tôi với hình ảnh động trong PageTransformer.

Nguyên văn câu trả lời này ở đây: https://stackoverflow.com/a/43638796/2867886

Nhưng tôi sẽ dán nó ở đây cho thuận tiện.

Giải pháp phù hợp nhất với tôi là vượt qua MotionEvent trong OnTouchListener giữa các trường hợp ViewPager. Đã cố gắng giả mạo kéo nhưng nó đã luôn luôn laggy và buggy - Tôi cần một hiệu ứng mịn, giống như thị sai.

Vì vậy, lời khuyên của tôi là triển khai View.OnTouchListener. Các MotionEvent phải được thu nhỏ để bù đắp cho sự khác biệt về chiều rộng.

public class SyncScrollOnTouchListener implements View.OnTouchListener { 

private final View syncedView; 

public SyncScrollOnTouchListener(@NonNull View syncedView) { 
    this.syncedView = syncedView; 
} 

@Override 
public boolean onTouch(View view, MotionEvent motionEvent) { 
    MotionEvent syncEvent = MotionEvent.obtain(motionEvent); 
    float width1 = view.getWidth(); 
    float width2 = syncedView.getWidth(); 

    //sync motion of two view pagers by simulating a touch event 
    //offset by its X position, and scaled by width ratio 
    syncEvent.setLocation(syncedView.getX() + motionEvent.getX() * width2/width1, 
      motionEvent.getY()); 
    syncedView.onTouchEvent(syncEvent); 
    return false; 
} 
} 

Sau đó đặt nó là của bạn ViewPager

sourcePager.setOnTouchListener(new SyncScrollOnTouchListener(targetPager)); 

Lưu ý rằng giải pháp này sẽ chỉ làm việc nếu cả hai máy nhắn tin có định hướng tương tự. Nếu bạn cần nó để hoạt động theo các hướng khác nhau - hãy điều chỉnh tọa độ của tọa độ Y23 thay vì X.

Có thêm một vấn đề cần lưu ý nữa.

Nó có thể được dễ dàng cố định bằng cách thêm một OnPageChangeListener để pager của chúng tôi

sourcePager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 
     @Override 
     public void onPageScrolled(int position, float positionOffset, 
            int positionOffsetPixels) { 
      //no-op 
     } 

     @Override 
     public void onPageSelected(int position) { 
      targetPager.setCurrentItem(position, true); 
     } 

     @Override 
     public void onPageScrollStateChanged(int state) { 
      //no-op 
     } 
    }); 
+0

tuyệt vời! cảm ơn bạn! :) và đẹp hơn rất nhiều so với giải pháp với OnPageChangeListener – Katharina

Các vấn đề liên quan