2010-07-03 38 views
5

Tôi có một Thư viện bao gồm nhiều ScrollViews mỗi trong số đó chiếm toàn bộ màn hình. vấn đề là onViewView của ScrollViews trả về true và do đó ngăn chặn bất kỳ khung nhìn nào khác trong DOM để xử lý cùng một sự kiện (được nuốt sau khi được xử lý ở cấp độ ScrollView). Do đó, Thư viện không cuộn nữa. Mặt khác, nếu tôi ghi đè lênTouchEvent như thế này:ScrollView và Gallery can thiệp

@Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     super.onTouchEvent(ev); 
     return false; // <<<<<<<<<<<<<<<<< 
    } 

thì Thư viện sẽ tiếp tục xử lý sự kiện nhưng SrollView không cuộn nữa. Dù bằng cách nào bạn cũng thua! hay bạn?

vấn đề nghe có vẻ khó hiểu nhưng tôi chắc chắn nếu bạn vấp ngã khi nó trong quá khứ u r gonna nhận ra nó ngay lập tức vì nó là một con quái vật đáng sợ!

cảm ơn

+0

hey nourdine đã bao giờ bạn có được điều này cố định? bạn đã sử dụng câu trả lời nào? – MikeIsrael

Trả lời

1

Tôi đã viết lớp tùy chỉnh này để có thể giải quyết vấn đề đó, và cho đến nay nó có vẻ là một giải pháp tốt. Đó không phải là hoàn hảo và tôi đã không bị mắc kẹt một ngã ba nó gọi nó là xong, nhưng hy vọng điều này có thể sử dụng một số với một ai đó :-)

import android.content.Context; 
import android.view.MotionEvent; 
import android.widget.Gallery; 
import android.widget.ScrollView; 

public class GalleryFriendlyScrollView extends ScrollView{ 

    private static int NONE = -1; 
    private static int DOSCROLLVIEW = 0; 
    private static int DOGALLERY = 1; 

    private float lastx = 0; 
    private float lasty = 0; 
    private float firstx = 0; 
    private float firsty = 0; 
    private float lastRawx = 0; 
    private float lastRawy = 0; 
    private int gestureRecipient = NONE; 
    private boolean donewithclick = true; 
    private Gallery parent = null; 
    private boolean firstclick = true; 

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

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     boolean retthis = true; 
     //Indicating a fresh click 
     if(donewithclick){ 
      firstx = ev.getX(); 
      firsty = ev.getY(); 
      lastx = firstx; 
      lasty = firsty; 
      lastRawx = ev.getRawX(); 
      lastRawy = ev.getRawY(); 
      donewithclick = false; 
      firstclick = true; 
     } 
     //We don't know where these gesture events are supposed to go to. 
     //We have movement on the x and/or why axes, so we can determine where they should go now. 
     if((gestureRecipient == NONE) && (lastx != ev.getX() || lasty != ev.getY())){ 
      //Determine whether there's more movement vertically or horizontally 
      float xdiff = ev.getX() - lastx; 
      float ydiff = ev.getY() - lasty; 
      if(xdiff < 0)xdiff = xdiff * -1; 
      if(ydiff < 0)ydiff = ydiff * -1; 
      if((xdiff) > (ydiff)){ 
       gestureRecipient = DOGALLERY; 
      } else { 
       gestureRecipient = DOSCROLLVIEW; 
      } 
     } 
     if(gestureRecipient == DOGALLERY){ 
      if(!firstclick){ 
       //After you drag the screen left or right a bit, the baseline for where it calculates 
       //x and y from changes, so we need to adjust the offset to our original baseline 
       float offsetx = (((ev.getX() - lastx) - (ev.getRawX() - lastRawx)) * -1); 
       float offsety = (((ev.getY() - lasty) - (ev.getRawY() - lastRawy)) * -1); 
       ev.offsetLocation(offsetx, offsety); 
      } 
      retthis = getGallery().onTouchEvent(ev); 
      firstclick = false; 
     } else if(gestureRecipient == DOSCROLLVIEW){ 
      retthis = super.onTouchEvent(ev); 
     } 
     if(ev.getAction() == MotionEvent.ACTION_UP){ 
      //User's finger has been lifted 
      if(((firstx == ev.getX()) && (firsty == ev.getY()))){ 
       //Since there isn't any movement in either direction, it's a click 
       getGallery().onSingleTapUp(ev); 
       super.onTouchEvent(ev); 
      } 
      donewithclick = true; 
      gestureRecipient = NONE; 
     } 
     //And record our coordinate data 
     lastx = ev.getX(); 
     lasty = ev.getY(); 
     lastRawx = ev.getRawX(); 
     lastRawy = ev.getRawY(); 
     return retthis; 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     getGallery().onTouchEvent(ev); 
     return super.onInterceptTouchEvent(ev); 
    } 

    private Gallery getGallery(){ 
     //Gets the gallery, in this case assuming the ScrollView is the direct child in the gallery. 
     //Adjust as needed 
     if(parent == null){ 
      parent = (Gallery) this.getParent(); 
     } 
     return parent; 
    } 
} 

Tôi rất muốn nghe kinh nghiệm của người dân với điều này, và bất kỳ đề xuất nào bạn có.

1

Tôi đã tìm thấy hành vi di chuyển thư viện của giải pháp kinh độ ít hơn tối ưu nên tôi đã tìm giải pháp khác. Cuối cùng, tôi tìm thấy một sản phẩm phù hợp nhất với tôi:

  1. Tạo lớp FriendlyGallery bằng cách mở rộng Thư viện và ghi đè lênTouchEvent, onInterceptTouchEvent, onScroll và onFling. Sử dụng onInterceptTouchEvent để "đăng ký" ScrollView hiện đang được hiển thị với FriendlyGallery và "điều khiển từ xa" ScrollView này (sử dụng nó scrollBy và fling Methods) trong onScroll và onFling của FriendlyGallery (velocityY of onFling = -1 * velocityY of fling !! !). Trả về true cho onTouchEvent để nắm bắt các sự kiện Touch ở đây!
  2. Tạo lớp FriendlyScrollView bằng cách mở rộng ScrollView và ghi đè lênTouchEvent, onInterceptTouchEvent chỉ trả lại "false" để giữ cho Sự kiện chạm không can thiệp vào "điều khiển từ xa" của bạn trong ScrollView.
4

Điều này đã khiến tôi đau đầu và tôi nghĩ tôi sẽ đăng một giải pháp dựa trên câu trả lời của chrisschell vì nó đã giúp tôi rất nhiều.

Đây là thư viện mới và được cải thiện.

public class FriendlyGallery extends Gallery { 
    FriendlyScrollView currScrollView; 

    public FriendlyGallery(Context context) { 
     super(context); 
    } 
    public FriendlyGallery(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     return super.onTouchEvent(ev); 
    } 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     currScrollView = getCurrScrollView(); 
     return super.onInterceptTouchEvent(ev);  
    } 
    @Override 
    public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
     if(currScrollView != null) 
      currScrollView.scrollBy(0, (int) distanceY); 
     return super.onScroll(e1, e2, distanceX, distanceY); 
    } 
    @Override 
    public boolean onFling(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
     if(currScrollView != null) 
      currScrollView.fling(-(int) distanceY); 
     return super.onFling(e1, e2, distanceX, distanceY);  
    } 

    private FriendlyScrollView getCurrScrollView() { 
     //I have a load more button that shouldn't be cast to a scrollview 
     int pos = getFirstVisiblePosition(); 
     if(pos != getAdapter().getCount()-1) 
      return (FriendlyScrollView)this.getSelectedView(); 
     else 
      return null; 
    } 
} 

Và chế độ xem cuộn.

public class FriendlyScrollView extends ScrollView { 

    public FriendlyScrollView(Context context) { 
     super(context); 
    } 
    public FriendlyScrollView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     return false; 
    } 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     return false;  
    } 
} 

Hy vọng điều này sẽ giúp và cảm ơn một lần nữa để chrisschell chỉ cho tôi đúng hướng.

+0

Tôi thấy những gì bạn đang nói về RajaSekhar. Bạn có nhớ đăng cách bạn chuyển tiếp các sự kiện nhấp chuột cho trẻ em để chúng tôi có câu trả lời hoàn chỉnh không? –

6

Đây là nỗ lực của tôi tại thư viện hoạt động với dọc ScrollViews.

Nó sử dụng trường hợp riêng của mình là GestureDetector và cấp dữ liệu với MotionEvents từ onInterceptTouchEvent.

Khi máy dò cử chỉ nhận dạng cuộn, chúng tôi xác định xem nó có nằm ngang hay dọc và khóa theo hướng cho đến khi cử chỉ kết thúc. Điều này tránh di chuyển chéo.

Nếu đó là cuộn ngang, onInterceptTouchEvent sẽ trả về true để các sự kiện chuyển động trong tương lai được chuyển đến thừa kế Gallery.onTouchEvent để thực hiện cuộn thực tế.

Máy dò cử chỉ riêng của Gallery (mGestureDetector trong Gallery.java) không nhận được tất cả sự kiện chuyển động và do đó đôi khi báo cáo các cuộn giấy bất ngờ khiến thư viện nhảy xung quanh. Tôi đã đặt trong một hack khó chịu mà loại bỏ những người.

Mã:

import android.content.Context; 
import android.util.AttributeSet; 
import android.view.GestureDetector; 
import android.view.MotionEvent; 
import android.widget.Gallery; 

public class BetterGallery extends Gallery { 
    /* This gets set when we detect horizontal scrolling */ 
    private boolean scrollingHorizontally = false; 

    /* This gets set during vertical scrolling. We use this to avoid detecting 
    * horizontal scrolling when vertical scrolling is already in progress 
    * and vice versa. */ 
    private boolean scrollingVertically = false; 

    /* Our own gesture detector, Gallery's mGestureDetector is private. 
    * We'll feed it with motion events from `onInterceptTouchEvent` method. */ 
    private GestureDetector mBetterGestureDetector; 

    public BetterGallery(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mBetterGestureDetector = new GestureDetector(new BetterGestureListener()); 
    } 

    public BetterGallery(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     mBetterGestureDetector = new GestureDetector(new BetterGestureListener()); 
    } 

    public BetterGallery(Context context) { 
     super(context); 
     mBetterGestureDetector = new GestureDetector(new BetterGestureListener()); 
    } 

    @Override 
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
      float velocityY) { 
     // Limit velocity so we don't fly over views 
     return super.onFling(e1, e2, 0, velocityY); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     // Documentation on this method's contract: 
     // http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent) 

     // Reset our scrolling flags if ACTION_UP or ACTION_CANCEL 
     switch (ev.getAction()) { 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      scrollingHorizontally = false; 
      scrollingVertically = false; 
     }  

     // Feed our gesture detector 
     mBetterGestureDetector.onTouchEvent(ev); 

     // Intercept motion events if horizontal scrolling is detected 
     return scrollingHorizontally; 
    } 


    @Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
     // Hack: eat jerky scrolls caused by stale state in mGestureDetector 
     // which we cannot directly access 
     if (Math.abs(distanceX) > 100) return false; 

     return super.onScroll(e1, e2, distanceX, distanceY); 
    } 


    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     // Reset our scrolling flags if ACTION_UP or ACTION_CANCEL 
     switch(event.getAction()) { 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      scrollingHorizontally = false; 
      scrollingVertically = false; 
     } 

     super.onTouchEvent(event); 
     return scrollingHorizontally; 
    } 

    private class BetterGestureListener implements GestureDetector.OnGestureListener { 

     @Override 
     public boolean onDown(MotionEvent arg0) { 
      return false; 
     } 

     @Override 
     public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { 
      return false; 
     } 

     @Override 
     public void onLongPress(MotionEvent arg0) { 
     } 

     @Override 
     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
      if (scrollingHorizontally || scrollingVertically) { 
       // We already know we're scrolling, ignore this callback. 
       // This avoids changing scrollingHorizontally/scrollingVertically 
       // flags mid-scroll. 
       return false; 
      } 

      scrollingHorizontally |= Math.abs(distanceX) > Math.abs(distanceY); 
      // It's a scroll, and if it's not horizontal, then it has to be vertical 
      scrollingVertically = !scrollingHorizontally; 

      return false; 
     } 

     @Override 
     public void onShowPress(MotionEvent arg0) { 

     } 

     @Override 
     public boolean onSingleTapUp(MotionEvent arg0) { 
      return false; 
     } 
    } 
} 

Cảnh báo: Từ "Better" trong tên lớp có khả năng gây nhầm lẫn!

Cập nhật:

Quên đề cập đến, tôi cũng đã thiết lập hoạt động để chuyển tiếp onTouchEvent của nó vào thư viện:

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    return mGallery.onTouchEvent(event); 
} 

Cập nhật 2:

Tôi đã thực hiện một số cải tiến cho mã này và đặt nó lên trên bitbucket. Ngoài ra còn có một ứng dụng ví dụ. Nó chứng tỏ rằng widget này có vấn đề với ListView như đứa trẻ: -/

enter image description here

Cập nhật 3:

Switched Gallery-HorizontalScrollView là lớp cơ sở cho widget tùy chỉnh của tôi. More on this here. Flings hoạt động, ListViews và ExpandableListViews khi trẻ em làm việc, được thử nghiệm trên Android 1.6, 2.2, 2.3.4. Hành vi của nó bây giờ khá gần với ứng dụng của Google, IMO.

Cập nhật 4:

Google đã công bố ViewPager!

+0

+1 giải pháp này làm việc cho tôi, tôi đề nghị nó :) –

+0

nhưng có rất ít vấn đề, đôi khi nó bị mắc kẹt hoặc không nhạy cảm –

+0

@MoshErsan: ban đầu quên đề cập đến tôi đang chuyển tiếp 'onTouchEvent' từ hoạt động tới thư viện. Điều đó có thể hữu ích. –

0

Làm thế nào về giải pháp này: Đơn giản chỉ cần xử lý các sự kiện trong cả hai, các ScrollViewGallery:

public class GalleryFriendlyScrollView extends ScrollView { 
    private Gallery fParent; 


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


    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     boolean superDone = super.onTouchEvent(ev); 
     // correct the location of the event because the gallery 
     // might have moved the scroll pane horizontally 
     ev.setLocation(ev.getX() + getLeft(), ev.getY()); 
     // dispatch the event also to the gallery 
     boolean galleryDone = getGallery().onTouchEvent(ev); 
     return superDone || galleryDone; 
    } 

    private Gallery getGallery() { 
     if (fParent == null) { 
      fParent = (Gallery)this.getParent(); 
     } 
     return fParent; 
    } 
} 
Các vấn đề liên quan