Android: how to detect when the scroll is over

I am using the onScroll method for GestureDetector.SimpleOnGestureListener to scroll a large bitmap on the canvas. When the scrolling is over, I want to redraw the bitmap if the user wants to scroll further ... from the edge of the bitmap, but I don’t see how to determine when the scroll ended (the user lifted his finger from the screen).

e2.getAction () always returns 2, so this is not a help. e2.getPressure seems to return fairly constant values ​​(around 0.25) until the final Scroll call, when the pressure seems to drop to around 0.13. I suppose I could detect this decrease in pressure, but it will be far from reliable.

There must be a better way: can someone help please?

+60
android android-canvas smooth-scrolling
Jan 18 '10 at 22:01
source share
12 answers

This is how I solved the problem. Hope this helps.

// 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); } 
+61
Sep 29 '10 at 1:09 april
source share

You should take a look at http://developer.android.com/reference/android/widget/Scroller.html . This can especially help (sorted by relevance):

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

This means that you need to create a scroller.

If you want to use touch, you can also use the GestureDetector and define your own canvas scroll. The following example creates a ScrollableImageView, and to use it you need to define the dimensions of your image. You can define your own scroll range, and after the scroll is completed, the image will be redrawn.

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

Depending on your code, you should consider invalidate (int l, int t, int r, int b); for invalidity.

+4
Feb 06 '10 at 13:21
source share

Returning to this after a few months, I now took a different approach: using a handler (as in the Android Snake example) to send a message to the application every 125 milliseconds, in which he was asked to check if there was a scroll and more than 100 milliseconds had passed since the last scroll events.

This seems to work very well, but if anyone can see any flaws or possible improvements, I should be grateful for them.

The corresponding code is in the MyView class:

 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; 

// Scrolling is complete, so paste the code here

// which calls the doDrawing () method

// to redraw the bitmap with re-centering where the scroll ends

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

// the code for drawing a large bitmap image of the buffer onto the presentation canvas // is positioned to account for any scrolling that is performed.

 } public void doDrawing() { 

// code to perform detailed (and time-consuming) drawing // into a bitmap image of a large buffer

// the next command resets the clock for checking time // the clock starts when the main action // calls this method when the application starts

  mTimeCheckHandler.sleep(timeCheckInterval); } 

// remainder of class MyView

}

and in class 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; 

// the following command calls the View onDraw method // which displays the bitmap of the buffer on the screen // is shifted to allow for scrolling

  [MyView].invalidate(); } 

// remainder of class MyGestureDetector

}

+1
Aug 19 '10 at 20:58
source share
 SimpleOnGestureListener.onFling() 

It seems that this happens when the scroll ends (i.e. the user allows the finger) that I use, and it works fine for me.

+1
Sep 30 '10 at 18:25
source share

I studied the same problem. I saw Akos Zhu answer your question. I created something similar, but with my version I noticed that it worked only for a regular scroll - this means that it does not generate a throw. But if the cast was indeed generated - regardless of whether I processed or not, then it did NOT detect "ACTION_UP" in "onTouchEvent". Now, maybe it was something with my implementation, but if it were, I could not understand why.

After further research, I noticed that during the flight "ACTION_UP" every time passed "onFling" in "e2". Therefore, I realized that, perhaps, that is why it was not processed in the "onTouchEvent instances".

To make me work for me, I needed to call the processing method "ACTION_UP" in "onFling", and then it worked for both types of scrolling. The following are the exact steps I took to implement in my application:

- initialized "gestureScrolling" with the boolean value "false" in the constructor.

-I set the value "true" to "onScroll"

-created an event processing method "ACTION_UP". Inside this event, I reset "gestureCrolling" to false, and then the rest of the processing I needed to do.

-in "onTouchEvent", if "ACTION_UP" and "gestureScrolling" = true are detected, then I called my method to handle "ACTION_UP"

-And the part that I did was different: I also called my method processing "ACTION_UP" inside "onFling".

+1
Dec 02 2018-10-12T00:
source share

I am sure that it is too late for you, however, it seems that I have found the right solution to your initial question and do not need an intention.

If you use a Scroller / OverScroller Object to scroll, you must check the return value from the following function.

 public boolean computeScrollOffset() 

to use

harvinder

+1
Mar 13 '12 at 11:18
source share

I did not do this myself, but looking at onTouch (), you always get the sequence 0 <2> 1, so the end should be 1 to lift your finger.

0
Jan 27
source share

I do not know Android, but looking at the documentation, it seems that Rob is right: Android ACTION_UP constant Try checking ACTION_UP with getAction ()?

Edit: what does e1.getAction () show? Does it return ACTION_UP? The documentation says that it contains an initial down event, so maybe it will also tell when the pointer is up

Edit: Only two things I can think of. Are you coming back at any time? This can prevent ACTION_UP

The only thing I will try is to have a separate event, possibly onDown, and set the flag inside onScroll, for example isScrolling. When ACTION_UP is set to onDown and isScrolling is set, you can do whatever you want, and reset isScrolling is false. That is, assuming that onDown is called along with onScroll, and getAction will return ACTION_UP during onDown

0
04 Feb '10 at 1:59
source share

I have not tried / used this, but an idea for an approach:

stop / interrupt canvas redrawing on EVERY scroll of events wait for 1 s, and then start redrawing the canvas on EVERY scroll.

this will only result in redrawing at the end of the scroll, since only the last scroll will actually be continuous to complete the redraw.

hope this idea helps you :)

0
Feb 06 2018-10-06T00
source share

Extract from onScroll event from GestureListener API: link text

public abstractionism boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) C: API Level 1

Returns * true if the event is consumed, otherwise false

It’s possible that after the event was destroyed, the action was completed and the user put a finger off the screen or at least finished this action onScroll

You can then use this in the IF statement to scan == true, and then start with the next step.

0
Feb 10 '10 at 11:52
source share

I think this will work the way you need

 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
Mar 22 '13 at 14:35
source share

This is what worked for me.

I enriched the existing GestureDetector.OnGestureListener with the onFingerUp () method. This listener does everything as a built-in GestureDetector, and it can also listen to the finger up event (this is not onFling (), since this is called only when the finger rises along with a quick hit).

 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; } } 
0
Apr 27 '15 at 14:47
source share



All Articles