web-dev-qa-db-ja.com

Android:スクロールの終了を検出する方法

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

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

より良い方法が必要です:誰でも助けてくれませんか?

65
prepbgg

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

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

http://developer.Android.com/reference/Android/widget/Scroller.html をご覧ください。特にこれは助けになる可能性があります(関連性によってソートされます):

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

これは、スクローラーを作成する必要があることを意味します。

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

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

コードに応じて、invalidate(int l、int t、int r、int b)を検討する必要があります。無効化のため。

4
user238801
SimpleOnGestureListener.onFling() 

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

3
Aron Cederholm

数か月後にこれに戻って、私は今、別のタックに従いました:ハンドラーを使用して(Android Snakeサンプル)、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;

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

    [MyView].invalidate();

}

//残りのMyGestureDetectorクラス

}

2
prepbgg

私はこれと同じ問題を調査していました。 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"を処理するメソッドも呼び出しました。

1
Mike

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

既存の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;
    }
}
1
pepan

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

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

public boolean computeScrollOffset() 

楽しい

ハービンダー

1
resp78

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

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

}
0
nautilusvn

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

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

0
Peter

私はこれを自分でやったことはありませんが、onTouch()を見ると、常にシーケンス0 <2> 1が得られるので、指を持ち上げるには1でなければなりません。

0
Rob Kent

GestureListener APIのonScrollイベントから抽出: link text

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

*イベントが消費される場合はtrue、そうでない場合はfalseを返します

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

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

0

Androidはわかりませんが、ドキュメントを見るとRobが正しいようです: Android ACTION_UP constant 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を返すと仮定します。

0
Bob

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

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

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

このアイデアがあなたを助けることを願っています:)

0
b0x0rz