1147

flingAndroid アプリケーションでジェスチャ検出を機能させたいと考えています。

私が持っているのは、GridLayout9 ImageViews を含む です。ソースはRomain Guys の Grid Layoutにあります。

私が取ったそのファイルは、Romain Guy のPhotostream アプリケーションからのもので、わずかに変更されています。

onClickListener単純なクリックの状況では、追加するfor eachを実装ImageViewするメインに設定するだけです。を認識するものを実装するのは、はるかに複雑に思えます。これは、スパンする可能性があるためだと思いますか?activityView.OnClickListenerflingviews

  • アクティビティが実装されている場合、 追加するビューまたはビューOnGestureListenerのジェスチャ リスナーとしてそれを設定する方法がわかりません。GridImage

    public class SelectFilterActivity extends Activity implements
       View.OnClickListener, OnGestureListener { ...
    
  • 私のアクティビティが実装されている場合、 方法OnTouchListenerはありません (パラメーターとして 2 つのイベントがあり、フリングが注目に値するかどうかを判断できます)。onFlingoverride

    public class SelectFilterActivity extends Activity implements
        View.OnClickListener, OnTouchListener { ...
    
  • カスタムを作成すると、そのViewようGestureImageViewに拡張され、ビューから a が発生したImageViewことをアクティビティに伝える方法がわかりません。flingいずれにせよ、これを試してみたところ、画面に触れてもメソッドが呼び出されませんでした。

これがビュー全体で機能する具体的な例が本当に必要です。何を、いつ、どのように添付すればよいlistenerですか?シングルクリックも検出できる必要があります。

// 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;
        }
    }
});

フリングをキャプチャするために、画面の上部に透明なビューを配置することはできますか?

inflateXML から子イメージ ビューを取得しないことを選択した場合GestureDetector、コンストラクター パラメーターとして、作成した新しいサブクラスに渡すことができImageViewますか?

flingこれは、検出を機能させようとしている非常に単純なアクティビティです: SelectFilterActivity (Adapted from photostream)

私はこれらの情報源を見てきました:

これまでのところ何もうまくいかず、いくつかの指針を期待していました.

4

18 に答える 18

215

上記の回答の1つは、さまざまなピクセル密度の処理について言及していますが、スワイプパラメーターを手動で計算することを提案しています。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)

これらの値を使用すると、アプリケーションとシステムの他の部分との間でフリングの「感触」がより一貫したものになることに気づきました。

于 2011-04-20T17:26:49.330 に答える
152

私はそれを少し異なって行い、実装する追加の検出器クラスを作成しましたView.onTouchListener

onCreate次のように、最も低いレイアウトに追加するだけです。

ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout);
lowestLayout.setOnTouchListener(activitySwipeDetector);

ここで、id.lowestLayoutは、レイアウト階層の最下位のビューのid.xxxであり、lowestLayoutはRelativeLayoutとして宣言されています。

そして、実際のアクティビティスワイプ検出器クラスがあります。

public class ActivitySwipeDetector implements View.OnTouchListener {

static final String logTag = "ActivitySwipeDetector";
private Activity activity;
static final int MIN_DISTANCE = 100;
private float downX, downY, upX, upY;

public ActivitySwipeDetector(Activity activity){
    this.activity = activity;
}

public void onRightSwipe(){
    Log.i(logTag, "RightToLeftSwipe!");
    activity.doSomething();
}

public void onLeftSwipe(){
    Log.i(logTag, "LeftToRightSwipe!");
    activity.doSomething();
}

public void onDownSwipe(){
    Log.i(logTag, "onTopToBottomSwipe!");
    activity.doSomething();
}

public void onUpSwipe(){
    Log.i(logTag, "onBottomToTopSwipe!");
    activity.doSomething();
}

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;

       // swipe horizontal?
        if(Math.abs(deltaX) > Math.abs(deltaY))
        {
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX > 0) { this.onRightSwipe(); return true; }
                if(deltaX < 0) { this.onLeftSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }
        // swipe vertical?
        else 
        {
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onDownSwipe(); return true; }
                if(deltaY > 0) { this.onUpSwipe(); return true; }
            }
            else {
                    Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    return false; // We don't consume the event
            }
        }

            return true;
        }
    }
    return false;
}

}

私にとって本当に良い作品です!

于 2011-04-21T10:24:21.823 に答える
98

Thomas Fankhauserのソリューションを少し修正して修復しました

システム全体は、 SwipeInterfaceActivitySwipeDetectorの 2 つのファイルで構成されます


SwipeInterface.java

import android.view.View;

public interface SwipeInterface {

    public void bottom2top(View v);

    public void left2right(View v);

    public void right2left(View v);

    public void top2bottom(View v);

}

検出器

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class ActivitySwipeDetector implements View.OnTouchListener {

    static final String logTag = "ActivitySwipeDetector";
    private SwipeInterface activity;
    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;

    public ActivitySwipeDetector(SwipeInterface activity){
        this.activity = activity;
    }

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

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

    public void onTopToBottomSwipe(View v){
        Log.i(logTag, "onTopToBottomSwipe!");
        activity.top2bottom(v);
    }

    public void onBottomToTopSwipe(View v){
        Log.i(logTag, "onBottomToTopSwipe!");
        activity.bottom2top(v);
    }

    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;

            // swipe horizontal?
            if(Math.abs(deltaX) > MIN_DISTANCE){
                // left or right
                if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
            }

            // swipe vertical?
            if(Math.abs(deltaY) > MIN_DISTANCE){
                // top or down
                if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; }
                if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; }
            }
            else {
                Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                v.performClick();
            }
        }
        }
        return false;
    }

}

次のように使用されます。

ActivitySwipeDetector swipe = new ActivitySwipeDetector(this);
LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout);
swipe_layout.setOnTouchListener(swipe);

また、実装ではSwipeInterfaceActivityからメソッドを実装する必要があり、 Swipe Eventが呼び出されたビューを見つけることができます。

@Override
public void left2right(View v) {
    switch(v.getId()){
        case R.id.swipe_layout:
            // do your stuff here
        break;
    }       
}
于 2012-01-10T16:11:19.260 に答える
69

上記のスワイプ ジェスチャ検出コードは非常に便利です。(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);
于 2011-02-18T09:45:16.207 に答える
39

ThomasFankhauserMarekSeberaによって提案された私のバージョンのソリューション(垂直スワイプを処理しません):

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

}
于 2012-07-18T01:00:52.337 に答える
30

この質問は古いもので、2011 年 7 月に Googleは Android 1.6 以降で動作する互換パッケージ、リビジョン 3)をリリースしました。ViewPagerこのGestureListener質問に対して投稿された回答は、Android ではあまり洗練されていません。Android ギャラリーで写真を切り替えたり、新しい Play マーケット アプリでビューを切り替えたりするために使用されるコードを探しているなら、間違いなくViewPager.

詳細については、次のリンクを参照してください。

于 2012-04-23T19:54:51.073 に答える
24

すべてのジェスチャに直接使用できる組み込みのインターフェイスがあります。
以下は、基本レベルのユーザー向けの説明です。 ここに画像の説明を入力 2 つのインポートがあり、両方が異なることを選択する際には注意してください ここに画像の説明を入力 ここに画像の説明を入力

于 2013-04-05T14:28:13.783 に答える
21

ViewConfiguration を使用するための Web (およびこのページ) 上のいくつかの提案があります。getScaledTouchSlop()を使用して、 のデバイス スケール値を取得しSWIPE_MIN_DISTANCEます。

getScaledTouchSlop()スワイプではなく、「スクロールしきい値」の距離を対象としています。スクロールしきい値距離は、「ページ間のスイング」しきい値距離よりも小さくする必要があります。たとえば、この関数は私の Samsung GS2 で 12 ピクセルを返し、このページで引用されている例は約 100 ピクセルです。

API レベル 8 (Android 2.2、Froyo) では、getScaledPagingTouchSlop()ページのスワイプを目的とした があります。私のデバイスでは、24 (ピクセル) が返されます。したがって、API レベル < 8 を使用している場合は、「2 * getScaledTouchSlop()」が「標準」のスワイプしきい値になるはずです。しかし、小さな画面の私のアプリケーションのユーザーは、それが少なすぎると私に言いました... 私のアプリケーションと同様に、垂直にスクロールし、水平にページを変更できます。提案された値を使用すると、スクロールする代わりにページを変更することがあります。

于 2012-02-09T21:33:44.973 に答える
16

また、マイナーな機能強化として。

try/catch ブロックの主な理由は、最初の動きで e1 が null になる可能性があるためです。try/catch に加えて、null と return のテストを含めます。次のように

if (e1 == null || e2 == null) return false;
try {
...
} catch (Exception e) {}
return false;
于 2011-04-12T21:14:06.220 に答える
15

ここには素晴らしい情報がたくさんあります。残念なことに、このフリング処理コードの多くは、さまざまなサイトにさまざまな完了状態で散らばっていますが、これは多くのアプリケーションにとって不可欠であると考えられています。

時間をかけて、適切な条件が満たされていることを確認するフリング リスナーを作成しました。フリングがページ フリングのしきい値を満たしていることを確認するためのチェックを追加するページ フリング リスナーを追加しました。これらのリスナーの両方を使用すると、フリングを水平軸または垂直軸に簡単に制限できます。スライド画像のビューでどのように使用されているかを確認できます。ここの人々がほとんどの調査を行ったことを認めます---使用可能なライブラリにまとめただけです。

ここ数日は、Android でのコーディングに初めて挑戦したことを表しています。さらに多くのことを期待してください。

于 2011-11-02T23:14:45.690 に答える
14

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をオーバーライドして、スワイプ イベントと他の検出イベント (クリックやロング クリックなど) の両方を許可する方法を示すも提供 します。ViewonTouch

スワイプ検出器

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);
    }
}

スワイプインターセプタービュー

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;
    }
}
于 2013-01-24T17:49:57.550 に答える
6

ジェスチャーとは、タッチ スクリーンとユーザーの間の対話をトリガーする微妙な動きです。画面に最初に触れてから最後の指が画面から離れるまでの時間持続します。

Android はGestureDetectorと呼ばれるクラスを提供します。これを使用して、タップダウンとアップ、垂直方向と水平方向のスワイプ (フリング)、長押しと短押し、ダブルタップなどの一般的なジェスチャを検出できます。リスナーをそれらにアタッチします。

Activityクラスに GestureDetector.OnDoubleTapListener (ダブルタップ ジェスチャ検出用) とGestureDetector.OnGestureListener インターフェイスを実装させ、すべての抽象メソッドを実装します。https://developer.android.com/training/gestures/detector.htmlにアクセスできます。礼儀

デモテスト用。GestureDetectorデモ

于 2015-07-23T05:50:58.093 に答える
4

すべての人へ: MotionEvent.ACTION_CANCEL のケースを忘れないでください:

ACTION_UP なしで 30% のスワイプで呼び出します

この場合は ACTION_UP に等しい

于 2013-12-28T12:13:04.880 に答える