我想让飞的手势检测工作在我的Android应用程序。

我有一个GridLayout,包含9个ImageViews。来源可以在这里找到:罗曼家伙的网格布局。

我从Romain Guy的Photostream应用程序中获取的文件只做了轻微的修改。

对于简单的点击情况,我只需要为每个ImageView设置onClickListener,我添加为实现View.OnClickListener的主要活动。实现能够识别投掷的东西似乎要复杂得多。我猜这是因为它可能跨越不同的观点?

If my activity implements OnGestureListener I don't know how to set that as the gesture listener for the Grid or the Image views that I add. public class SelectFilterActivity extends Activity implements View.OnClickListener, OnGestureListener { ... If my activity implements OnTouchListener then I have no onFling method to override (it has two events as parameters allowing me to determine if the fling was noteworthy). public class SelectFilterActivity extends Activity implements View.OnClickListener, OnTouchListener { ... If I make a custom View, like GestureImageView that extends ImageView I don't know how to tell the activity that a fling has occurred from the view. In any case, I tried this and the methods weren't called when I touched the screen.

我真的需要一个跨视图工作的具体例子。我应该在什么时候以及如何附加这个侦听器?我还需要能够检测单个点击。

// Gesture detection
mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        int dx = (int) (e2.getX() - e1.getX());
        // don't accept the fling if it's too short
        // as it may conflict with a button push
        if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.absvelocityY)) {
            if (velocityX > 0) {
                moveRight();
            } else {
                moveLeft();
            }
            return true;
        } else {
            return false;
        }
    }
});

是否有可能在我的屏幕上方放置一个透明的视图来捕捉投掷?

如果我选择不从XML膨胀我的子图像视图,我可以通过手势检测器作为构造函数参数到我创建的ImageView的新子类吗?

这是一个非常简单的活动,我试图让飞动检测工作:SelectFilterActivity(改编自photostream)。

我一直在看这些资料:

检测手势-教程 SDK文档 计算器的代码

到目前为止,什么都没用,我希望能得到一些指导。


当前回答

在web(和本页)上有一些主张使用ViewConfiguration.getScaledTouchSlop()为SWIPE_MIN_DISTANCE设置一个设备缩放值。

getScaledTouchSlop()用于“滚动阈值”距离,而不是滑动距离。滚动阈值距离必须小于“页面之间的摆动”阈值距离。例如,这个函数在我的三星GS2上返回12个像素,而本页引用的示例大约是100个像素。

与API级别8 (Android 2.2, Froyo),你有getScaledPagingTouchSlop(),用于页面滑动。 在我的设备上,它返回24(像素)。所以如果你的API级别< 8,我认为“2 * getScaledTouchSlop()”应该是“标准”的滑动阈值。 但是我的小屏幕应用程序的用户告诉我,它太少了……在我的应用程序中,你可以垂直滚动,水平切换页面。有了建议的值,他们有时会改变页面,而不是滚动。

其他回答

感谢幕府将军,他的代码我适应了我的情况。

让你的活动像往常一样实现onclicklistener:

public class SelectFilterActivity extends Activity implements OnClickListener {

  private static final int SWIPE_MIN_DISTANCE = 120;
  private static final int SWIPE_MAX_OFF_PATH = 250;
  private static final int SWIPE_THRESHOLD_VELOCITY = 200;
  private GestureDetector gestureDetector;
  View.OnTouchListener gestureListener;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /* ... */

    // Gesture detection
    gestureDetector = new GestureDetector(this, new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
      public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
      }
    };

  }

  class MyGestureDetector extends SimpleOnGestureListener {
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
      try {
        if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
          return false;
        // right to left swipe
        if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
          Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
        } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
          Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
        }
      } catch (Exception e) {
         // nothing
      }
      return false;
    }

    @Override
    public boolean onDown(MotionEvent e) {
      return true;
    }
  }
}

将你的手势监听器附加到你添加到主布局中的所有视图上;

// Do this for each view added to the grid
imageView.setOnClickListener(SelectFilterActivity.this); 
imageView.setOnTouchListener(gestureListener);

敬畏地看着你被覆盖的方法被击中,包括activity的onClick(View v)和手势监听器的onFling。

public void onClick(View v) {
  Filter f = (Filter) v.getTag();
  FilterFullscreenActivity.show(this, input, f);
}

“抛舞”后的舞蹈是可选的,但也是鼓励的。

您可以使用droidQuery库来处理投掷、单击、长时间单击和自定义事件。实现是建立在我之前的答案下面,但droidQuery提供了一个光滑的,简单的语法:

//global variables    private boolean isSwiping = false;
private SwipeDetector.Direction swipeDirection = null;
private View v;//must be instantiated before next call.

//swipe-handling code
$.with(v).swipe(new Function() {
    @Override
    public void invoke($ droidQuery, Object... params) {
        if (params[0] == SwipeDetector.Direction.START)
            isSwiping = true;
        else if (params[0] == SwipeDetector.Direction.STOP) {
            if (isSwiping) {                    isSwiping = false;
                if (swipeDirection != null) {
                    switch(swipeDirection) {
                        case DOWN :                                //TODO: Down swipe complete, so do something
                            break;
                        case UP :
                            //TODO: Up swipe complete, so do something
                            break;
                        case LEFT :
                            //TODO: Left swipe complete, so do something
                            break;
                        case RIGHT :
                            //TODO: Right swipe complete, so do something
                            break;
                        default :                                break;
                    }
                }                }
        }
        else {
            swipeDirection = (SwipeDetector.Direction) params[0];
        }
    }
});

原来的答案

这个答案使用了其他答案的组合。它由SwipeDetector类组成,该类有一个用于监听事件的内部接口。我还提供了一个RelativeLayout来展示如何覆盖一个视图的onTouch方法,以允许滑动事件和其他检测到的事件(如单击或长时间单击)。

SwipeDetector

package self.philbrown;

import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * Detect Swipes on a per-view basis. Based on original code by Thomas Fankhauser on StackOverflow.com,
 * with adaptations by other authors (see link).
 * @author Phil Brown
 * @see <a href="http://stackoverflow.com/questions/937313/android-basic-gesture-detection">android-basic-gesture-detection</a>
 */
public class SwipeDetector implements View.OnTouchListener
{
    /**
     * The minimum distance a finger must travel in order to register a swipe event.
     */
    private int minSwipeDistance;

    /** Maintains a reference to the first detected down touch event. */
    private float downX, downY;

    /** Maintains a reference to the first detected up touch event. */
    private float upX, upY;

    /** provides access to size and dimension contants */
    private ViewConfiguration config;

    /**
     * provides callbacks to a listener class for various swipe gestures.
     */
    private SwipeListener listener;

    public SwipeDetector(SwipeListener listener)
    {
        this.listener = listener;
    }


    /**
     * {@inheritDoc}
     */
    public boolean onTouch(View v, MotionEvent event)
    {
        if (config == null)
        {
                config = ViewConfiguration.get(v.getContext());
                minSwipeDistance = config.getScaledTouchSlop();
        }

        switch(event.getAction())
        {
        case MotionEvent.ACTION_DOWN:
            downX = event.getX();
            downY = event.getY();
            return true;
        case MotionEvent.ACTION_UP:
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            // swipe horizontal?
            if(Math.abs(deltaX) > minSwipeDistance)
            {
                // left or right
                if (deltaX < 0)
                {
                        if (listener != null)
                        {
                                listener.onRightSwipe(v);
                                return true;
                        }
                }
                if (deltaX > 0)
                {
                        if (listener != null)
                        {
                                listener.onLeftSwipe(v);
                                return true;
                        }
                }
            }

            // swipe vertical?
            if(Math.abs(deltaY) > minSwipeDistance)
            {
                // top or down
                if (deltaY < 0)
                {
                        if (listener != null)
                        {
                                listener.onDownSwipe(v);
                                return true;
                        }
                }
                if (deltaY > 0)
                {
                        if (listener != null)
                        {
                                listener.onUpSwipe(v);
                                return true;
                        }
                }
            }
        }
        return false;
    }

    /**
     * Provides callbacks to a registered listener for swipe events in {@link SwipeDetector}
     * @author Phil Brown
     */
    public interface SwipeListener
    {
        /** Callback for registering a new swipe motion from the bottom of the view toward its top. */
        public void onUpSwipe(View v);
        /** Callback for registering a new swipe motion from the left of the view toward its right. */
        public void onRightSwipe(View v);
        /** Callback for registering a new swipe motion from the right of the view toward its left. */
        public void onLeftSwipe(View v);
        /** Callback for registering a new swipe motion from the top of the view toward its bottom. */
        public void onDownSwipe(View v);
    }
}

Swipe Interceptor视图

package self.philbrown;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

import com.npeinc.module_NPECore.model.SwipeDetector;
import com.npeinc.module_NPECore.model.SwipeDetector.SwipeListener;

/**
 * View subclass used for handling all touches (swipes and others)
 * @author Phil Brown
 */
public class SwipeInterceptorView extends RelativeLayout
{
    private SwipeDetector swiper = null;

    public void setSwipeListener(SwipeListener listener)
    {
        if (swiper == null)
            swiper = new SwipeDetector(listener);
    }

    public SwipeInterceptorView(Context context) {
        super(context);
    }

    public SwipeInterceptorView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SwipeInterceptorView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e)
    {
        boolean swipe = false, touch = false;
        if (swiper != null)
            swipe = swiper.onTouch(this, e);
        touch = super.onTouchEvent(e);
        return swipe || touch;
    }
}

上面的滑动手势检测代码非常有用!但是,您可能希望通过使用以下相对值(REL_SWIPE)而不是绝对值(SWIPE_)来使该解决方案密度不可知。

DisplayMetrics dm = getResources().getDisplayMetrics();

int REL_SWIPE_MIN_DISTANCE = (int)(SWIPE_MIN_DISTANCE * dm.densityDpi / 160.0f);
int REL_SWIPE_MAX_OFF_PATH = (int)(SWIPE_MAX_OFF_PATH * dm.densityDpi / 160.0f);
int REL_SWIPE_THRESHOLD_VELOCITY = (int)(SWIPE_THRESHOLD_VELOCITY * dm.densityDpi / 160.0f);

这里有很多很好的信息。不幸的是,许多这种抛抛处理代码以不同的完成状态分散在不同的站点上,尽管人们会认为这对许多应用程序来说是必不可少的。

我已经花时间创建了一个fling侦听器,用于验证是否满足适当的条件。我添加了一个页抛监听器,它添加了更多检查,以确保页抛满足页抛的阈值。这两个侦听器都允许您轻松地将投掷限制在水平轴或垂直轴上。你可以看到它是如何在滑动图像的视图中使用的。我承认这里的人已经做了大部分的研究——我只是把它们放在一个可用的图书馆里。

这几天是我第一次尝试在Android上编写代码;期待更多。

在web(和本页)上有一些主张使用ViewConfiguration.getScaledTouchSlop()为SWIPE_MIN_DISTANCE设置一个设备缩放值。

getScaledTouchSlop()用于“滚动阈值”距离,而不是滑动距离。滚动阈值距离必须小于“页面之间的摆动”阈值距离。例如,这个函数在我的三星GS2上返回12个像素,而本页引用的示例大约是100个像素。

与API级别8 (Android 2.2, Froyo),你有getScaledPagingTouchSlop(),用于页面滑动。 在我的设备上,它返回24(像素)。所以如果你的API级别< 8,我认为“2 * getScaledTouchSlop()”应该是“标准”的滑动阈值。 但是我的小屏幕应用程序的用户告诉我,它太少了……在我的应用程序中,你可以垂直滚动,水平切换页面。有了建议的值,他们有时会改变页面,而不是滚动。