ScrollView и галерея мешают - PullRequest
5 голосов
/ 03 июля 2010

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

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

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

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

Ответы [ 5 ]

6 голосов
/ 13 июля 2011

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

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

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

Если это горизонтальная прокрутка, onInterceptTouchEvent вернет истину, так что будущие события движения перейдут к унаследованной 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;
        }
    }
}

Предупреждение: слово «лучше» в названии класса может вводить в заблуждение!

Обновление:

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

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

Обновление 2:

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

enter image description here

Обновление 3:

Переключен с Gallery наHorizontalScrollView в качестве базового класса для моего пользовательского виджета. Подробнее об этом здесь .Работает Flings, ListViews и ExpandableListViews работают как дети, протестировано на Android 1.6, 2.2, 2.3.4.Его поведение теперь очень похоже на поведение приложений Google, IMO.

Обновление 4:

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

4 голосов
/ 25 февраля 2011

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

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

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

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

1 голос
/ 15 декабря 2010

Я обнаружил, что прокрутка в галерее для решения atraudes не оптимальна, поэтому я искал другое решение. Наконец я нашел тот, который идеально подходит для меня:

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

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

0 голосов
/ 08 июня 2012

Как насчет этого решения: просто обработайте событие как в 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...