2010-07-03 3 views
5

У меня есть Галерея, состоящая из многих ScrollView, каждая из которых занимает весь экран. проблема заключается в том, что функция onCouchEvent ScrollViews возвращает true и, следовательно, предотвращает любое другое представление в DOM для обработки одного и того же события (которое проглатывается после обработки на уровне ScrollView). В результате Галерея больше не прокручивается. С другой стороны, если я переопределить onTouchEvent так:ScrollView и Gallery вмешиваются

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

то галерея получает на события для обработки, но SrollView не суммируется и не свитка больше. В любом случае вы проиграете! или вы?

проблема звучит озадачивающе, но я уверен, что если бы вы наткнулись на нее в прошлом, то сразу узнаете ее, как она чертовски чертовски!

благодаря

+0

эй nourdine вы когда-нибудь это исправить? какой ответ вы использовали? – MikeIsrael

ответ

1

Я написал этот пользовательский класс, чтобы быть в состоянии справиться с этой проблемой, и до сих пор, кажется, порядочным решением. Это не идеально, и я не воткнул вилку в нем назвать это сделано, но, надеюсь, это может быть полезен кому-то :-)

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; 
    } 
} 

Я хотел бы услышать от опыта людей с этим, и любые предложения, которые у вас есть.

1

Я нашел решение прокрутки галереи, использующее решение атак, менее оптимальным, поэтому я искал другое решение. Наконец-то я нашел тот, который прекрасно работает для меня:

  1. Создать класс FriendlyGallery путем расширения галереи и перезаписать onTouchEvent, onInterceptTouchEvent, OnScroll и onFling. Используйте onInterceptTouchEvent, чтобы «зарегистрировать» текущий отображаемый ScrollView с помощью FriendlyGallery и «пульт дистанционного управления» этого ScrollView (используя его scrollBy и fling Methods) внутри onScroll и onFling of FriendlyGallery (speedY ofFling = -1 * speedY of fling !! !). Верно true для onTouchEvent для захвата Touch Events здесь!
  2. Создайте класс FriendlyScrollView, расширив ScrollView и перезапишив onTouchEvent, onInterceptTouchEvent просто вернет «false», чтобы события Touch не мешали вашему «удаленному контролю» ScrollView.
4

Это дало мне головные боли, и я подумал, что отправлю решение, основанное на ответе Крисчелла, потому что это очень помогло мне.

Вот новая и улучшенная галерея.

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; 
    } 
} 

И прокрутка.

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;  
    } 
} 

Надеюсь, что это поможет и еще раз поблагодарить вас за то, что вы указали мне в правильном направлении.

+0

Я вижу, что вы говорите о РаджаСехаре. Не возражаете ли вы опубликовать, как вы перенаправляете события кликов детям, поэтому у нас есть полный ответ? –

6

Это моя попытка галереи, которая работает с вертикальной ScrollViews.

Он использует свой собственный экземпляр GestureDetector и передает его MotionEvents от onInterceptTouchEvent.

Когда детектор жестов распознает свиток, мы определяем, горизонтально или вертикально, и заблокируйте его в направлении до тех пор, пока жест не будет закончен. Это позволяет избежать диагональной прокрутки.

Если это горизонтальный прокрутка, onInterceptTouchEvent вернет true, чтобы события будущего движения перешли в унаследованную Gallery.onTouchEvent, чтобы выполнить фактическую прокрутку.

Gallery Собственный детектор жестов (mGestureDetector in Gallery.java) не получает все события движения и, следовательно, иногда сообщает огромные неожиданные свитки, которые заставляют галерею прыгать. Я вложил неприятный взлом, который отбрасывает их.

Код:

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; 
     } 
    } 
} 

Предупреждение: слово "лучше" в имени класса, скорее всего, вводит в заблуждение!

Update:

забыл упомянуть, я также установил деятельность направить свою onTouchEvent в галерею:

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

Update 2:

Я сделал некоторые улучшения этого кода и выставили его на bitbucket. Существует также пример приложения. Это свидетельствует о том, что этот виджет имеет проблемы с ListView, как ребенок: -/

enter image description here

Update 3:

Switched от Gallery к HorizontalScrollView в качестве базового класса для моего пользовательского виджета. More on this here. Flings work, ListViews и ExpandableListViews работают как дети, тестируются на Android 1.6, 2.2, 2.3.4. Его поведение сейчас очень близко к действию приложений Google, IMO.

Update 4:

Google опубликовал ViewPager!

+0

+1 это решение сработало для меня, я рекомендую его :) –

+0

, но есть небольшая проблема, когда-то она застряла или была нечувствительной –

+0

@MoshErsan: изначально забыл упомянуть, что я пересылаю 'onTouchEvent' из активности в галерею. Это может помочь. –

0

Как насчет этого решения: Просто обрабатывать событие в обоих, в ScrollView и Gallery:

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; 
    } 
} 
Смежные вопросы