SimpleOnGestureListener 代码在 Android 2.2 中不起作用

发布于 2024-10-20 08:24:30 字数 7324 浏览 1 评论 0原文

我编写了一些代码来实现垂直滑动 画廊小部件。它在 Android 1.5 和 1.6 中运行良好,但在 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() 并返回时 确实,我确实可以通过滑动来工作。但是当我这样做时 长按和单击时不会显示上下文菜单 也不起作用...单击图库中的项目会导致 画廊跳转,当我点击一个时我没有得到任何反馈 画廊中的项目。它会立即使该项目成为所选项目 项目并将其移动到中心。

我查看了 1.6、2.1 和 2.2 之间的 API 差异报告, 没有看到任何可能导致这种情况的重要因素 打破...

我做错了什么?

编辑:

了解画廊嵌套在几个布局中也可能会有所帮助,如下所示(这不是完整的布局......它只是为了显示该画廊所在位置的层次结构):

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

I have some code that I wrote to implement a vertical swipe on a
Gallery widget. It works great in Android 1.5 and 1.6 but does not
work in Android 2.2 (I have yet to try it with 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);
       }
}

And to be able to get my gallery to detect the onFling I have the
following:

   m_gallery.setOnTouchListener(new SwipeUpDetector());

In Android 1.5 and 1.6 this works great. In Android 2.2 onFling() is
never called. In looking around on Google and StackOverflow I found
one possible solution was to implement onDown() and return true.

However, I am also listening to single clicks and have a context menu
listener set up on this gallery. When I implement onDown() and return
true I do indeed get the swipe to work. But when I do this the
context menu doesn't display on a long click and the single clicks
don't work either... Clicking on items in the gallery cause the
gallery to jump around and I don't get any feedback when I click on an
item in the gallery. It just immediately makes that item the selected
item and moves it to the center.

I looked at the API differences report between 1.6, 2.1, and 2.2 and
didn't see anthing of significance that could have caused this to
break...

What am I doing wrong?

EDIT:

It might also be helpful to know that the gallery is nested inside a couple layouts as follows (this isn't a complete layout... it is just intended to show the hierarchy of where this Gallery lives):

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

EDIT #2:

Here are the requested layouts... There are two of them, for reusability purposes. Here is the first one, which is the main activity's layout:

<?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>

And here is the layout file for com.magouyaware.appswipe.TitledGallery... This is nothing more than a RelativeLayout subclass for the purpose of controlling several views as a single item in the code and for reusability:

<?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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

屌丝范 2024-10-27 08:24:30

通过在我的实现中实现onSingleTapConfirmedonDoubleTaponLongPress,我还能够接收单击/双击和长按< em>SimpleOnGestureListener(同时从 onDown 返回 true)。

关于为什么我们应该重写 onDown 方法。我认为这与问题 #8233 有关。据报道一年前针对2.1版本。由于到目前为止只有 10 人加星,我猜它不会在不久的将来得到修复。

更新

事实证明,该问题是由 ScrollViewGallery 的组合以及 OnTouchListener 的使用引起的。 Gallery 本身实现了 OnGestureListener 并封装了 GestureDetector,当我们设置 OnTouchListener 时,它会被禁用,从而导致奇怪的画廊行为有时。另一方面,如果我们只是对 Gallery 组件进行子类化并在其 onLongPress/onFling 方法中执行长按/滑动检测,则父 ScrollView 将拦截垂直移动事件,从而防止 onFling 调用此类事件。解决方案是重写 Gallery.dispatchTouchEvent 并调用 requestDisallowInterceptTouchEvent(true) 用于画廊父级。

总结一下:如果您想检测 Gallery 的滑动(长按、双击等)(并可能将其放置在 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);
  }
}

I was able to receive single/double clicks and long click as well by implementing onSingleTapConfirmed, onDoubleTap and onLongPress in my implementation of SimpleOnGestureListener (while returning true from onDown).

Concerning why we should override onDown method. I think it is related to the issue #8233. It was reported one year ago against 2.1 version. Since only 10 people starred it so far I guess it would not be fixed in the near future.

UPDATE

It turned out that the issue was caused by the combination of ScrollView and Gallery and usage of OnTouchListener. Gallery itself implements OnGestureListener and encapsulates GestureDetector which is disabled when we set our OnTouchListener, resulting in a strange gallery behavior sometimes. On the other hand if we just subclass Gallery component and perform long-click/swipe detection in its onLongPress/onFling methods the parent ScrollView will intercept vertical move events preventing onFling call for such events. The solution is to override Gallery.dispatchTouchEvent and call requestDisallowInterceptTouchEvent(true) for gallery parent.

To summarize: if you want to detect swipes (long-, double-clicks etc.) for the Gallery (and possibly place it inside the ScrollView) use the custom component provided below instead of 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);
  }
}
樱花坊 2024-10-27 08:24:30

如果您不处理向下,您将不会收到与该向下事件链接的任何事件(滚动、快速滑动、向上)。所以你必须返回true。

我试图理解为什么,但还是失败了。也许是因为 SimpleOnGestureListener 默认返回 false,并且外部布局中的一些新的 2.2 优化感觉您不想要该事件。你不再是这一系列事件的有效目标。

为了让您的 longPress 正常工作,您不能在检测器中实现 onLongPress 事件并调用使菜单出现的代码吗?

If you do not handle the down you will not get any event (scroll, fling, up) linked to this down event. So you must return true.

I tried to understand why but I failed yet. Maybe since SimpleOnGestureListener returns false by default, and some new 2.2 optimizations in the outer Layout feels you do not want the event. You are not a valid target anymore for the chain of events.

To get your longPress working, can't you implement the onLongPress event in your detector and call the code that makes your menu appear?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文