我想让飞的手势检测工作在我的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文档 计算器的代码

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


当前回答

这个问题有点老了,2011年7月谷歌发布了兼容性包,修订版3),其中包括与android1.6以上的ViewPager。在Android上,针对这个问题发布的GestureListener的答案感觉不太优雅。如果你正在寻找用于在Android Gallery中切换照片或在新的Play Market应用程序中切换视图的代码,那么它肯定是ViewPager。

以下是更多信息的链接:

http://android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html http://mobile.tutsplus.com/tutorials/android/android-user-interface-design-horizontal-view-paging/ http://thepseudocoder.wordpress.com/2011/10/05/android-page-swiping-using-viewpager/

其他回答

上面的一个答案提到了处理不同的像素密度,但建议手动计算滑动参数。值得注意的是,你可以使用ViewConfiguration类从系统中获得合理的缩放值:

final ViewConfiguration vc = ViewConfiguration.get(getContext());
final int swipeMinDistance = vc.getScaledPagingTouchSlop();
final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity();
final int swipeMaxOffPath = vc.getScaledTouchSlop();
// (there is also vc.getScaledMaximumFlingVelocity() one could check against)

我注意到,使用这些值可以使应用程序和系统其他部分之间的“感觉”更加一致。

我需要一个更通用的类,我采取了Tomas的类,并添加了一个接口,发送事件到您的活动或片段。 它会在构造函数上注册监听器,所以确保你实现了接口,否则就会出现ClassCastException。该接口返回定义的四个最终整型之一 并将返回激活它的视图。

import android.app.Activity;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class SwipeDetector implements View.OnTouchListener{

    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;
    public final static int RIGHT_TO_LEFT=1;
    public final static int LEFT_TO_RIGHT=2;
    public final static int TOP_TO_BOTTOM=3;
    public final static int BOTTOM_TO_TOP=4;
    private View v;

    private onSwipeEvent swipeEventListener;


    public SwipeDetector(Activity activity,View v){
        try{
            swipeEventListener=(onSwipeEvent)activity;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",activity.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }
    public SwipeDetector(Fragment fragment,View v){
        try{
            swipeEventListener=(onSwipeEvent)fragment;
        }
        catch(ClassCastException e)
        {
            Log.e("ClassCastException",fragment.toString()+" must implement SwipeDetector.onSwipeEvent");
        } 
        this.v=v;
    }


    public void onRightToLeftSwipe(){   
        swipeEventListener.SwipeEventDetected(v,RIGHT_TO_LEFT);
    }

    public void onLeftToRightSwipe(){   
        swipeEventListener.SwipeEventDetected(v,LEFT_TO_RIGHT);
    }

    public void onTopToBottomSwipe(){   
        swipeEventListener.SwipeEventDetected(v,TOP_TO_BOTTOM);
    }

    public void onBottomToTopSwipe(){
        swipeEventListener.SwipeEventDetected(v,BOTTOM_TO_TOP);
    }

    public boolean onTouch(View v, MotionEvent event) {
        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;

            //HORIZONTAL SCROLL
            if(Math.abs(deltaX) > Math.abs(deltaY))
            {
                if(Math.abs(deltaX) > MIN_DISTANCE){
                    // left or right
                    if(deltaX < 0) 
                    {
                        this.onLeftToRightSwipe();
                        return true;
                    }
                    if(deltaX > 0) {
                        this.onRightToLeftSwipe();
                        return true; 
                    }
                }
                else {
                    //not long enough swipe...
                    return false; 
                }
            }
            //VERTICAL SCROLL
            else 
            {
                if(Math.abs(deltaY) > MIN_DISTANCE){
                    // top or down
                    if(deltaY < 0) 
                    { this.onTopToBottomSwipe();
                    return true; 
                    }
                    if(deltaY > 0)
                    { this.onBottomToTopSwipe(); 
                    return true;
                    }
                }
                else {
                    //not long enough swipe...
                    return false;
                }
            }

            return true;
        }
        }
        return false;
    }
    public interface onSwipeEvent
    {
        public void SwipeEventDetected(View v , int SwipeType);
    }

}

这个问题有点老了,2011年7月谷歌发布了兼容性包,修订版3),其中包括与android1.6以上的ViewPager。在Android上,针对这个问题发布的GestureListener的答案感觉不太优雅。如果你正在寻找用于在Android Gallery中切换照片或在新的Play Market应用程序中切换视图的代码,那么它肯定是ViewPager。

以下是更多信息的链接:

http://android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html http://mobile.tutsplus.com/tutorials/android/android-user-interface-design-horizontal-view-paging/ http://thepseudocoder.wordpress.com/2011/10/05/android-page-swiping-using-viewpager/

Thomas Fankhauser和Marek Sebera提出的我的解决方案版本(不处理垂直滑动):

SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void onLeftToRight(View v);

    public void onRightToLeft(View v);
}

ActivitySwipeDetector.java

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    private float downX, downY;
    private long timeDown;
    private final float MIN_DISTANCE;
    private final int VELOCITY;
    private final float MAX_OFF_PATH;

    public ActivitySwipeDetector(Context context, SwipeInterface activity){
        this.activity = activity;
        final ViewConfiguration vc = ViewConfiguration.get(context);
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density;
        VELOCITY = vc.getScaledMinimumFlingVelocity();
        MAX_OFF_PATH = MIN_DISTANCE * 2;            
    }

    public void onRightToLeftSwipe(View v){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.onRightToLeft(v);
    }

    public void onLeftToRightSwipe(View v){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.onLeftToRight(v);
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN: {
            Log.d("onTouch", "ACTION_DOWN");
            timeDown = System.currentTimeMillis();
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            Log.d("onTouch", "ACTION_UP");
            long timeUp = System.currentTimeMillis();
            float upX = event.getX();
            float upY = event.getY();

            float deltaX = downX - upX;
            float absDeltaX = Math.abs(deltaX); 
            float deltaY = downY - upY;
            float absDeltaY = Math.abs(deltaY);

            long time = timeUp - timeDown;

            if (absDeltaY > MAX_OFF_PATH) {
                Log.i(logTag, String.format("absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH));
                return v.performClick();
            }

            final long M_SEC = 1000;
            if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) {
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            } else {
                Log.i(logTag, String.format("absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE)));
                Log.i(logTag, String.format("absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC)));
            }

        }
        }
        return false;
    }

}

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

让你的活动像往常一样实现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);
}

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