Вот моя попытка галереи, которая работает с вертикалью 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 как дочерний: - /
Обновление 3:
Переключен с Gallery
наHorizontalScrollView
в качестве базового класса для моего пользовательского виджета. Подробнее об этом здесь .Работает Flings, ListViews и ExpandableListViews работают как дети, протестировано на Android 1.6, 2.2, 2.3.4.Его поведение теперь очень похоже на поведение приложений Google, IMO.
Обновление 4:
Google опубликовал ViewPager !