68

GestureDetector.SimpleOnGestureListener の onScroll メソッドを使用して、キャンバス上の大きなビットマップをスクロールしています。スクロールが終了したら、ユーザーがさらにスクロールしたい場合に備えてビットマップを再描画したい...ビットマップの端から外れたが、スクロールがいつ終了したかを検出する方法がわからない(ユーザーが指を離した)画面から)。

e2.getAction() は常に値 2 を返すように見えるため、これは役に立ちません。e2.getPressure は、圧力が約 0.13 に低下するように見える最後の onScroll 呼び出しまで、かなり一定の値 (約 0.25) を返すようです。この圧力の低下は検出できたと思いますが、絶対確実というわけではありません。

もっと良い方法があるに違いありません: 誰か助けてくれませんか?

4

14 に答える 14

70

これが私が問題を解決した方法です。お役に立てれば。

// declare class member variables
private GestureDetector mGestureDetector;
private OnTouchListener mGestureListener;
private boolean mIsScrolling = false;


public void initGestureDetection() {
        // Gesture detection
    mGestureDetector = new GestureDetector(new SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            handleDoubleTap(e);
            return true;
        }

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

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // i'm only scrolling along the X axis
            mIsScrolling = true;                
            handleScroll(Math.round((e2.getX() - e1.getX())));
            return true;
        }

        @Override
        /**
         * Don't know why but we need to intercept this guy and return true so that the other gestures are handled.
         * https://code.google.com/p/android/issues/detail?id=8233
         */
        public boolean onDown(MotionEvent e) {
            Log.d("GestureDetector --> onDown");
            return true;
        }
    });

    mGestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            if (mGestureDetector.onTouchEvent(event)) {
                return true;
            }

            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mIsScrolling ) {
                    Log.d("OnTouchListener --> onTouch ACTION_UP");
                    mIsScrolling  = false;
                    handleScrollFinished();
                };
            }

            return false;
        }
    };

    // attach the OnTouchListener to the image view
    mImageView.setOnTouchListener(mGestureListener);
}
于 2010-09-29T01:09:31.670 に答える
4

http://developer.android.com/reference/android/widget/Scroller.htmlをご覧ください。特にこれは役に立ちます(関連性でソート):

isFinished();
computeScrollOffset();
getFinalY(); getFinalX(); and getCurrY() getCurrX()
getDuration()

これは、Scroller を作成する必要があることを意味します。

タッチを使用したい場合は、GestureDetector を使用して独自のキャンバス スクロールを定義することもできます。次のサンプルでは、​​ScrollableImageView を作成しています。これを使用するには、画像の測定値を定義する必要があります。独自のスクロール範囲を定義でき、スクロールが終了すると画像が再描画されます。

http://www.anddev.org/viewtopic.php?p=31487#31487

コードによっては、invalidate(int l, int t, int r, int b); を考慮する必要があります。無効化のために。

于 2010-02-06T13:21:46.257 に答える
3
SimpleOnGestureListener.onFling() 

スクロールが終了すると(つまり、ユーザーが指を離すと)発生するようです。それが私が使用しているものであり、私にとってはうまく機能します。

于 2010-09-30T18:25:54.297 に答える
2

数か月後にこれに戻ると、私は別の方法に従いました。(Android Snake サンプルのように) Handler を使用して、125 ミリ秒ごとにアプリにメッセージを送信し、スクロールが開始されたかどうかを確認するように促し、最後のスクロール イベントから 100 ミリ秒以上経過したかどうか。

これはかなりうまくいっているようですが、誰かが欠点や改善の可能性を確認できる場合は、それらを聞いて感謝する必要があります.

関連するコードは MyView クラスにあります。

public class MyView extends android.view.View {

...

private long timeCheckInterval = 125; // milliseconds
private long scrollEndInterval = 100;
public long latestScrollEventTime;
public boolean scrollInProgress = false;

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

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
        long now = System.currentTimeMillis();
        if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval)) {
                    scrollInProgress = false;

// スクロールが終了したので、ここにコードを挿入します

// doDrawing() メソッドを呼び出す

// スクロールが終了した位置でビットマップを再中央に再描画します

                    [ layout or view ].invalidate();
        }
        this.sleep(timeCheckInterval);
        }

        public void sleep(long delayMillis) {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
            }
    }
}

@Override protected void onDraw(Canvas canvas){
        super.onDraw(canvas);

// 大きなバッファ ビットマップをビューのキャンバスに描画するコード // 進行中のスクロールを考慮して配置

}

public void doDrawing() {

// 詳細な (そして時間のかかる) 描画を行うコード // 大きなバッファ ビットマップに

// 次の命令はタイム チェック クロックをリセットします // クロックは、アプリの起動時にメイン アクティビティがこのメソッドを呼び出したときに // 最初に開始されます

        mTimeCheckHandler.sleep(timeCheckInterval);
}

// MyView クラスの残りの部分

}

および MyGestureDetector クラスで

public class MyGestureDetector extends SimpleOnGestureListener {

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {

    [MyView].scrollInProgress = true;
        long now = System.currentTimeMillis();  
    [MyView].latestScrollEventTime =now;

    [MyView].scrollX += (int) distanceX;
    [MyView].scrollY += (int) distanceY;

// 次の命令により、View の onDraw メソッドが呼び出されます // これにより、バッファ ビットマップが画面に描画されます // スクロールを考慮してシフトされます

    [MyView].invalidate();

}

// MyGestureDetector クラスの残りの部分

}

于 2010-08-19T20:58:42.737 に答える
1

これが私のために働いたものです。

既存のGestureDetector.OnGestureListeneronFingerUp()メソッドで強化しました。このリスナーは組み込みの GestureDetector としてすべてを実行し、指アップ イベントをリッスンすることもできます (これは onFling() ではありません。これは、素早いスワイプ アクションと共に指が持ち上げられた場合にのみ呼び出されるためです)。

import android.content.Context;
import android.os.Handler;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class FingerUpGestureDetector extends GestureDetector {
    FingerUpGestureDetector.OnGestureListener fListener;
    public FingerUpGestureDetector(Context context, OnGestureListener listener) {
        super(context, listener);
        fListener = listener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, OnGestureListener fListener) {
        super(context, listener);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, OnGestureListener fListener) {
        super(context, listener, handler);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused, OnGestureListener fListener) {
        super(context, listener, handler, unused);
        this.fListener = fListener;
    }

    public interface OnGestureListener extends GestureDetector.OnGestureListener {
        boolean onFingerUp(MotionEvent e);
    }

    public static class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements FingerUpGestureDetector.OnGestureListener {
        @Override
        public boolean onFingerUp(MotionEvent e) {
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (super.onTouchEvent(ev)) return true;
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            return fListener.onFingerUp(ev);
        }
        return false;
    }
}
于 2015-04-27T14:47:21.123 に答える
1

遅すぎると思いますが、元の質問に対する正しい解決策を見つけたようで、意図は必要ありません。

スクロールに Scroller/OverScroller オブジェクトを使用している場合は、次の関数からの戻り値を確認する必要があります。

public boolean computeScrollOffset() 

楽しい

ハービンダー

于 2012-03-13T11:18:02.487 に答える
1

これは必要に応じて機能すると思います

protected class SnappingGestureDetectorListener extends SimpleOnGestureListener{

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){
        boolean result = super.onScroll(e1, e2, distanceX, distanceY);

        if(!result){
            //Do what you need to do when the scrolling stop here
        }

        return result;
    }

}
于 2013-03-22T14:35:54.310 に答える
1

私はこの同じ問題を調べていました。あなたの質問に対する Akos Cz の回答を見ました。私は似たようなものを作成しましたが、私のバージョンでは、通常のスクロール (フリングを生成しないもの) でしか機能しないことに気付きました。しかし、フリングが生成された場合、フリングを処理したかどうかに関係なく、「onTouchEvent」で「ACTION_UP」を検出しませんでした。これは私の実装に問題があったのかもしれませんが、もしそうなら理由がわかりませんでした。

さらに調査した結果、フリング中に「ACTION_UP」が「e2」の「onFling」に毎回渡されていることに気付きました。そのため、これらのインスタンスで「onTouchEvent」で処理されなかったのはそのためだと思いました。

それを機能させるには、「onFling」で「ACTION_UP」を処理するメソッドを呼び出すだけで、両方のタイプのスクロールで機能しました。以下は、アプリに実装するために行った正確な手順です。

- コンストラクターで「gestureScrolling」ブール値を「false」に初期化。

-「onScroll」で「true」に設定しました

-「ACTION_UP」イベントを処理するメソッドを作成しました。そのイベント内で、「gestureSCrolling」を false にリセットしてから、必要な残りの処理を行いました。

-「onTouchEvent」で、「ACTION_UP」が検出され、「gestureScrolling」= true の場合、「ACTION_UP」を処理するメソッドを呼び出しました。

-そして、私が行った部分が異なっていたのは、「onFling」内で「ACTION_UP」を処理するメソッドも呼び出したことです。

于 2010-12-02T02:43:16.627 に答える
0

GestureListener API の onScroll イベントから抽出:リンク テキスト

public abstract boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 以降: API レベル 1

戻り値 * イベントが消費された場合は true、そうでない場合は false

おそらく、イベントが消費されると、アクションは終了し、ユーザーは画面から指を離すか、少なくともこの onScroll アクションを終了します

これを IF ステートメントで使用して == true をスキャンし、次のアクションを開始できます。

于 2010-02-10T11:52:32.987 に答える
0

私は Android を知りませんが、ドキュメントを見ると Rob が正しいようです: Android ACTION_UP 定数getAction() から ACTION_UP をチェックしてみてください。

編集: e1.getAction() は何を示していますか? ACTION_UP を返すことはありますか? ドキュメントによると、最初のダウンイベントを保持しているため、ポインターがアップしたときにも通知される可能性があります

編集:私が考えることができるのはあと2つだけです。どの時点でも false を返していますか? ACTION_UPを妨げる可能性があります

私が試みる他の唯一のことは、別のイベント、おそらく onDown を用意し、isScrolling などの onScroll 内にフラグを設定することです。ACTION_UP が onDown に指定され、isScrolling が設定されている場合は、必要に応じて isScrolling を false にリセットできます。つまり、onDown が onScroll と共に呼び出され、getAction が onDown 中に ACTION_UP を返すと仮定します。

于 2010-02-04T01:59:45.967 に答える
0

私はこれを自分で行ったことはありませんが、onTouch() を見ると、常にシーケンス 0<2>1 を取得するため、指を持ち上げるには最後を 1 にする必要があります。

于 2010-01-27T13:36:13.670 に答える
0

ジェスチャー検出器に追加機能を追加しようとする私の試み。誰かが自分の時間をより有効に活用するのに役立つことを願っています...

https://gist.github.com/WildOrangutan/043807ddbd68978179b7cea3b53c71e8

于 2018-08-09T13:51:18.327 に答える
0

私はこれを試したり使用したりしていませんが、アプローチのアイデアです:

すべてのスクロールイベントでキャンバスの再描画を停止/中断し、1秒待機してから、すべてのスクロールでキャンバスの再描画を開始します。

これにより、再描画が完了するまで実際に中断されないのは最後のスクロールのみであるため、スクロールの最後にのみ再描画が実行されます。

このアイデアがお役に立てば幸いです:)

于 2010-02-06T18:06:02.550 に答える