Nice programing

Android : 스크롤이 언제 끝났는지 감지하는 방법

nicepro 2020. 11. 18. 21:29
반응형

Android : 스크롤이 언제 끝났는지 감지하는 방법


GestureDetector.SimpleOnGestureListener의 onScroll 메서드를 사용하여 캔버스에서 큰 비트 맵을 스크롤합니다. 스크롤이 끝났을 때 사용자가 비트 맵의 ​​가장자리에서 더 멀리 스크롤하려는 경우 비트 맵을 다시 그리고 싶지만 스크롤이 끝났을 때 감지하는 방법을 볼 수 없습니다 (사용자가 손가락을 뗀 경우 화면에서).

e2.getAction ()은 항상 값 2를 반환하는 것처럼 보이므로 도움이되지 않습니다. e2.getPressure는 압력이 약 0.13으로 떨어지는 것처럼 보일 때 최종 onScroll 호출까지 상당히 일정한 값 (약 0.25)을 반환하는 것 같습니다. 이 압력의 감소를 감지 할 수 있다고 생각하지만 이것은 절대로 완벽하지는 않습니다.

더 나은 방법이 있어야합니다 : 누구든지 도와 줄 수 있습니까?


문제를 해결 한 방법은 다음과 같습니다. 도움이 되었기를 바랍니다.

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

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); 무효화를 위해.


SimpleOnGestureListener.onFling() 

스크롤이 끝날 때 (즉, 사용자가 손가락을 놓을 때) 발생하는 것 같습니다. 이것이 제가 사용하고있는 것이며 저에게 잘 작동합니다.


몇 달 후 다시 돌아와서 저는 이제 다른 방법을 따랐습니다. 핸들러 (Android Snake 샘플에서와 같이)를 사용하여 125 밀리 초마다 앱에 메시지를 전송하여 Scroll이 시작되었는지 확인하라는 메시지를 표시하고 마지막 스크롤 이벤트 이후 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 클래스

}


나는이 같은 문제를 조사하고 있었다. 귀하의 질문에 대한 Akos Cz 답변을 보았습니다. 비슷한 것을 만들었지 만 내 버전에서는 일반 스크롤에서만 작동한다는 것을 알았습니다. 즉, 플링을 생성하지 않는 것입니다. 그러나 fling이 생성 된 경우-내가 fling을 처리했는지 여부와 관계없이 "onTouchEvent"에서 "ACTION_UP"을 감지하지 못했습니다. 이제 이것은 내 구현과 관련된 것일 수도 있지만 그 이유를 알 수 없었습니다.

추가 조사 결과, 플링 중에 매번 "e2"의 "onFling"에 "ACTION_UP"이 전달되는 것을 발견했습니다. 그래서 나는 그 인스턴스에서 "onTouchEvent"에서 처리되지 않는 이유가 틀림 없다고 생각했습니다.

To make it work for me I only had to call a method to handle the "ACTION_UP" in "onFling" and then it worked for both types of scrolling. Below are the exact steps I took to implement in my app:

-initialized a "gestureScrolling" boolean to "false" in a constructor.

-I set it to "true" in "onScroll"

-created a method to handle the "ACTION_UP" event. Inside that event, I reset "gestureSCrolling" to false and then did the rest of the processing I needed to do.

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

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


I am sure it is too late for you, however, it seems I have found the right solution to your original question and not necessary the intention.

If you are using Scroller/OverScroller Object for scrolling you should check the return value from the following function.

public boolean computeScrollOffset() 

enjoy

harvinder


This is what worked for me.

I've enriched the existing GestureDetector.OnGestureListener with onFingerUp() method. This listener does everything as the built-in GestureDetector and it can also listen to the finger up event (it's not onFling() as this is called only when the finger is lifted up along with a quick swipe action).

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

I haven't done this myself but looking at onTouch() you always get a sequence 0<2>1, so the end has to be a 1 for finger lift.


I don't know Android, but looking at the documentation it seems Rob is right: Android ACTION_UP constant Try checking for ACTION_UP from getAction()?

Edit: What does e1.getAction() show? Does it ever return ACTION_UP? The documentation says it holds the initial down event, so maybe it'll also notify when the pointer is up

Edit: Only two more things I can think of. Are you returning false at any point? That may prevent ACTION_UP

The only other thing I'd try is to have a seperate event, maybe onDown, and set a flag within onScroll such as isScrolling. When ACTION_UP is given to onDown and isScrolling is set then you could do whatever you want and reset isScrolling to false. That is, assuming onDown gets called along with onScroll, and getAction will return ACTION_UP during onDown


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

stop / interrupt redrawing canvas on EVERY scroll event wait 1s and then start redrawing canvas on EVERY scroll.

this will lead to performing the redraw only at scroll end as only the last scroll will actually be uninterrupted for the redraw to complete.

hope this idea helps you :)


Extract from the onScroll event from GestureListener API: link text

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

Returns * true if the event is consumed, else false

Perhaps once the event has been consumed, the action is finished and the user has taken their finger off the screen or at the least finished this onScroll action

You can then use this in an IF statement to scan for == true and then commence with the next action.


I think this will work as 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;
    }

}

My attempt at adding additional functionality to gesture detector. Hope it helps someone put his time to better use...

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

참고URL : https://stackoverflow.com/questions/2089552/android-how-to-detect-when-a-scroll-has-ended

반응형