Код SimpleOnGestureListener не работает в Android 2.2 - PullRequest
6 голосов
/ 04 марта 2011

У меня есть код, который я написал для реализации вертикального пролистывания в виджете Галерея.Он прекрасно работает в Android 1.5 и 1.6, но не работает в Android 2.2 (я еще не пробовал его с 2.1).

public class SwipeUpDetector extends SimpleOnGestureListener
implements OnTouchListener
{
       private GestureDetector m_detector;

       public SwipeUpDetector()
       {
               m_detector = new GestureDetector(m_context, this);
       }

       @Override
       public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
       {
               if (Math.abs(e1.getX() - e2.getX()) < s_swipeMaxOffPath &&
                       e1.getY() - e2.getY() >= s_swipeMinDistance &&
                       Math.abs(velocityY) >= s_swipeMinVelocity)
               {
                       int pos = m_gallery.pointToPosition((int)e1.getX(), (int)e2.getY());
                       startAnimation(pos);

                       return true;
               }

               return false;
       }

       @Override
       public boolean onTouch(View v, MotionEvent event)
       {
               return m_detector == null ? false : m_detector.onTouchEvent(event);
       }
}

И чтобы моя галерея могла обнаружить onFling, у меня естьследующее:

   m_gallery.setOnTouchListener(new SwipeUpDetector());

В Android 1.5 и 1.6 это прекрасно работает.В Android 2.2 onFling () никогда не вызывается.Оглядываясь на Google и StackOverflow, я обнаружил, что одним из возможных решений было реализовать onDown () и вернуть true.

Однако я также прослушиваю отдельные щелчки и настраиваю прослушиватель контекстного меню в этой галерее.Когда я реализую onDown () и возвращаю true, я действительно заставляю свайп работать.Но когда я делаю это, контекстное меню не отображается при длинном щелчке, и одиночные щелчки тоже не работают ... Нажатие на элементы в галерее вызывает скачок галереи, и я не получаю никакой обратной связи, когда янажмите на элемент в галерее.Он просто сразу делает этот элемент выбранным и перемещает его в центр.

Я посмотрел отчет об отличиях API между 1.6, 2.1 и 2.2 и не увидел значимой значимости, которая могла бы вызвать это.перерыв ...

Что я делаю не так?

РЕДАКТИРОВАТЬ:

Также может быть полезно знать, что галерея вложена в пару макетов следующим образом (этоне полный макет ... он просто предназначен для того, чтобы показать иерархию того, где живет эта Галерея):

 <ScrollView>
      <LinearLayout>
           <RelativeLayout> <!-- This relative layout is a custom one that I subclassed -->
                <Gallery />
           </RelativeLayout>
      </LinearLayout>
 </ScrollView>

РЕДАКТИРОВАТЬ # 2:

Вот запрошенные макеты ..Есть два из них, для целей повторного использования.Вот первый, который является макетом основного действия:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:myns="http://com.magouyaware/appswipe"
    android:id="@+id/main_layout_id"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center_horizontal"
    android:scrollbarAlwaysDrawVerticalTrack="false"
>
    <LinearLayout 
        android:id="@+id/appdocks_layout_id"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginTop="10dp"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:gravity="center"
        android:background="@null"
    >
        <com.magouyaware.appswipe.TitledGallery
            android:id="@+id/running_gallery_layout_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            myns:gallery_title="@string/running_title"
        />

        <com.magouyaware.appswipe.TitledGallery
            android:id="@+id/recent_gallery_layout_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            myns:gallery_title="@string/recent_title"
        />

        <com.magouyaware.appswipe.TitledGallery
            android:id="@+id/favs_gallery_layout_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            myns:gallery_title="@string/favs_title"
        />

        <com.magouyaware.appswipe.TitledGallery
            android:id="@+id/service_gallery_layout_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            myns:gallery_title="@string/service_title"
        />

        <com.magouyaware.appswipe.TitledGallery
            android:id="@+id/process_gallery_layout_id"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            myns:gallery_title="@string/process_title"
        />

        <include 
            android:id="@+id/indeterminate_progress_layout_id" 
            layout="@layout/indeterminate_progress_layout" 
        />
    </LinearLayout>
</ScrollView>

А вот файл макета для com.magouyaware.appswipe.TitledGallery ... Это не более чем подкласс RelativeLayout для этой целиуправления несколькими представлениями как одним элементом в коде и возможностью повторного использования:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/titled_gallery_main_layout_id"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_vertical"
    android:layout_gravity="center_vertical"
    android:background="@null"
>
    <LinearLayout
        android:id="@+id/titled_gallery_expansion_layout_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:focusable="true"
        android:clickable="true"
        android:gravity="center_vertical"
    >
        <ImageView
            android:id="@+id/titled_gallery_expansion_image_id"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:duplicateParentState="true"
            android:clickable="false"
        />

        <TextView
            style="@style/TitleText"
            android:id="@+id/titled_gallery_title_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="left"
            android:paddingLeft="1sp"
            android:paddingRight="10sp"
            android:textColor="@drawable/titled_gallery_text_color_selector"
            android:duplicateParentState="true"
            android:clickable="false"
        />
    </LinearLayout>

    <Gallery
        android:id="@+id/titled_gallery_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/titled_gallery_expansion_layout_id"
        android:layout_alignWithParentIfMissing="true"
        android:spacing="5sp"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:unselectedAlpha=".5"
        android:focusable="false"
    />

    <TextView 
        style="@style/SubTitleText"
        android:id="@+id/titled_gallery_current_text_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/titled_gallery_id"
        android:layout_alignWithParentIfMissing="true"
        android:gravity="center_horizontal"
    />
</RelativeLayout>

Ответы [ 2 ]

1 голос
/ 14 марта 2011

Мне удалось получить одинарный / двойной щелчок и длинный щелчок, реализовав onSingleTapConfirmed , onDoubleTap и onLongPress в моей реализации SimpleOnGestureListener (при возврате true из onDown ).

Относительно того, почему мы должны переопределить onDown метод. Я думаю, что это связано с проблемой # 8233 . Об этом сообщается год назад против версии 2.1. Пока только 10 человек снялись в нем, я думаю, это не будет исправлено в ближайшем будущем.

UPDATE

Оказалось, что проблема была вызвана комбинацией ScrollView и Gallery и использованием OnTouchListener. Gallery сам реализует OnGestureListener и инкапсулирует GestureDetector, который отключается, когда мы устанавливаем наш OnTouchListener , что иногда приводит к странному поведению галереи. С другой стороны, если мы просто подклассируем компонент Gallery и выполняем обнаружение длинных щелчков / пролистываний в его методах onLongPress / onFling, родительский ScrollView будет перехватывать события вертикального перемещения, предотвращая вызов onFling для таких событий. Решение состоит в том, чтобы переопределить Gallery.dispatchTouchEvent и вызвать requestDisallowInterceptTouchEvent(true) для родителя галереи.

Подводя итог: если вы хотите обнаружить пролистывания (длинные, двойные щелчки и т. Д.) Для галереи (и, возможно, поместить ее в ScrollView), используйте предоставленный ниже пользовательский компонент вместо GestureDetector / OnTouchListener.

public class FlingGallery extends android.widget.Gallery implements OnDoubleTapListener {

  private static final int SWIPE_MIN_VELOCITY = 30;   // 30dp, set to the desired value

  private static final int SWIPE_MIN_DISTANCE = 50;   // 50dp, set to the desired value

  private static final int SWIPE_MAX_OFF_PATH = 40;   // 40dp, set to the desired value

  private final float mSwipeMinDistance;

  private final float mSwipeMaxOffPath;

  private final float mSwipeMinVelocity;

  public FlingGallery(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    float density = context.getResources().getDisplayMetrics().density;
    this.mSwipeMinDistance = density * SWIPE_MIN_DISTANCE;
    this.mSwipeMaxOffPath = density * SWIPE_MAX_OFF_PATH;
    this.mSwipeMinVelocity = density * SWIPE_MIN_VELOCITY;
  }

  public FlingGallery(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.galleryStyle);
  }

  public FlingGallery(Context context) {
    this(context, null);
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    final ViewParent parent;
    if (ev.getAction() == MotionEvent.ACTION_MOVE && (parent = getParent()) != null) {
      parent.requestDisallowInterceptTouchEvent(true);  // this will be passed up to the root view, i.e. ScrollView in our case
    }
    return super.dispatchTouchEvent(ev);
  }

  @Override
  public boolean onDoubleTap(MotionEvent e) {
  // Your double-tap handler...
    return true;
  }

  @Override
  public boolean onDoubleTapEvent(MotionEvent e) {
    return false;
  }

  @Override
  public boolean onSingleTapConfirmed(MotionEvent e) {
  // Your single-tap handler...
    return true;
  }    

  @Override
  public void onLongPress(MotionEvent event) {
  // Your long-press handler...
    super.onLongPress(event);
  }

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    if (e1 == null) {
      return super.onFling(e1, e2, velocityX, velocityY);
    }
    float dx = e2.getX() - e1.getX();
    float dy = e2.getY() - e1.getY();
    if (abs(dx) < mSwipeMaxOffPath && abs(velocityY) > mSwipeMinVelocity && abs(dy) > mSwipeMinDistance) {
      if (dy > 0) {
        // Your from-top-to-bottom handler...
      } else {
        // Your from-bottom-to-top handler...
      }
    } else if (abs(dy) < mSwipeMaxOffPath && abs(velocityX) > mSwipeMinVelocity && abs(dx) > mSwipeMinDistance) {
      if (dx > 0) {
        // Your from-left-to-right handler...
      } else {
        // Your from-right-to-left handler...
      }
    }
    return super.onFling(e1, e2, velocityX, velocityY);
  }
}
0 голосов
/ 14 марта 2011

Если вы не справитесь с понижением, вы не получите никакого события (прокрутка, бросок вверх), связанного с этим событием вниз.Таким образом, вы должны вернуть истину.

Я пытался понять, почему, но мне пока не удалось.Возможно, так как SimpleOnGestureListener возвращает false по умолчанию, и некоторые новые оптимизации 2.2 во внешнем макете чувствуют, что вы не хотите событие.Вы больше не действительная цель для цепочки событий.

Чтобы ваша longPress работала, разве вы не можете реализовать событие onLongPress в своем детекторе и вызвать код, который заставляет ваше меню появиться?

...