Android: Erkennen, wann ScrollView den Bildlauf beendet

84

Ich verwende eine ScrollViewin Android und wo der sichtbare Teil der ScrollViewdie gleiche Größe wie eine der Zellen in der hat Scrollview. Jede "Zelle" hat die gleiche Höhe. Ich versuche also, nach dem Scrollen in Position zu rasten ScrollView.

Momentan erkenne ich, wann der Benutzer das berührt hat ScrollViewund wann er von dort aus mit dem Scrollen und Ausarbeiten begonnen hat, aber es ist ziemlich fehlerhaft. Es muss auch funktionieren, wenn der Benutzer es nur bewegt und es scrollt und dann verlangsamt.

Auf dem iPhone gibt es eine Funktion, die so etwas wie ist, didDecelerateund dort kann ich jeden gewünschten Code ausführen, wenn der ScrollViewBildlauf abgeschlossen ist. Gibt es so etwas mit Android? Oder gibt es einen Code, den ich mir ansehen könnte, um einen besseren Weg zu finden?

Ich habe mir die Android-Dokumente angesehen und konnte so etwas nicht finden.

user1053691
quelle

Antworten:

98

Ich musste kürzlich die von Ihnen beschriebene Funktion implementieren. Was ich getan habe, war eine Runnable-Überprüfung, ob die ScrollView aufgehört hat zu scrollen, indem der von getScrollY () zurückgegebene Wert verglichen wurde, als das onTouchEvent zum ersten Mal mit dem Wert ausgelöst wurde, der nach einer durch die Variable newCheck definierten Zeit zurückgegeben wurde .

Siehe Code unten (Arbeitslösung):

public class MyScrollView extends ScrollView{

private Runnable scrollerTask;
private int initialPosition;

private int newCheck = 100;
private static final String TAG = "MyScrollView";

public interface OnScrollStoppedListener{
    void onScrollStopped();
}

private OnScrollStoppedListener onScrollStoppedListener;

public MyScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);

    scrollerTask = new Runnable() {

        public void run() {

            int newPosition = getScrollY();
            if(initialPosition - newPosition == 0){//has stopped

                if(onScrollStoppedListener!=null){

                    onScrollStoppedListener.onScrollStopped();
                }
            }else{
                initialPosition = getScrollY();
                MyScrollView.this.postDelayed(scrollerTask, newCheck);
            }
        }
    };
}

public void setOnScrollStoppedListener(MyScrollView.OnScrollStoppedListener listener){
    onScrollStoppedListener = listener;
}

public void startScrollerTask(){

    initialPosition = getScrollY();
    MyScrollView.this.postDelayed(scrollerTask, newCheck);
}

}

Dann habe ich:

scroll.setOnTouchListener(new OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_UP) {

                scroll.startScrollerTask();
            }

            return false;
        }
});
scroll.setOnScrollStoppedListener(new OnScrollStoppedListener() {

        public void onScrollStopped() {

            Log.i(TAG, "stopped");

        }
});

Übrigens habe ich einige Ideen aus anderen Antworten verwendet, um dies in meiner App zu tun. Hoffe das hilft. Fragen können Sie gerne stellen. Prost.

tulio84z
quelle
11
Danke, habe seit Ewigkeiten nach so etwas gesucht. Wird stattdessen für die HorizontalScrollView verwendet, was (offensichtlich) auch funktioniert. Nur getScrollY () muss durch getScrollX ()
Schnodahipfe
1
Versuchte sie alle hier. Dies ist die EINZIGE, die funktioniert.
Mike6679
22

Hier ist noch eine weitere Lösung für den IMHO fehlenden OnEndScroll-Ereignisfehler in der ScrollView.

Es ist inspiriert von hambonious Antwort. Fügen Sie diese Klasse einfach in Ihr Projekt ein (ändern Sie das Paket entsprechend Ihrem eigenen) und verwenden Sie die folgende XML-Datei

package com.thecrag.components.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class ResponsiveScrollView extends ScrollView {

    public interface OnEndScrollListener {
        public void onEndScroll();
    }

    private boolean mIsFling;
    private OnEndScrollListener mOnEndScrollListener;

    public ResponsiveScrollView(Context context) {
        this(context, null, 0);
    }

    public ResponsiveScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ResponsiveScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);
        mIsFling = true;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);
        if (mIsFling) {
            if (Math.abs(y - oldY) < 2 || y >= getMeasuredHeight() || y == 0) {
                if (mOnEndScrollListener != null) {
                    mOnEndScrollListener.onEndScroll();
                }
                mIsFling = false;
            }
        }
    }

    public OnEndScrollListener getOnEndScrollListener() {
        return mOnEndScrollListener;
    }

    public void setOnEndScrollListener(OnEndScrollListener mOnEndScrollListener) {
        this.mOnEndScrollListener = mOnEndScrollListener;
    }

}

Ändern Sie den Paketnamen erneut entsprechend Ihrem Projekt

<com.thecrag.components.ui.ResponsiveScrollView
        android:id="@+id/welcome_scroller"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/welcome_scroll_command_help_container"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/welcome_header_text_thecrag"
        android:layout_margin="6dp">
    ....
</com.thecrag.components.ui.ResponsiveScrollView>
Konzept
quelle
2
+1 Schöne, einfache Antwort. Ich wechsle den Differenzwert 2auf 0.5fdas Original zu reflektieren ScrollView‚s - MAX_SCROLL_FACTORWert.
Phil
2
Diese Implementierung ist nett, aber fehlgeschlagen, wenn die Bildlaufansicht beim Start nicht gefüllt ist. Hier ist eine Problemumgehung (für eine horizontale Bildlaufansicht ersetzen Sie also alle x durch y, um eine vertikale zu erhalten): @Override protected void onScrollChanged (int x, int y, int oldX, int oldY) {super.onScrollChanged (x, y) , oldX, oldY); if (Math.abs (x - oldX) <SCROLL_FINISHED_THRESHOLD && x! = lastStopX || x> = getMeasuredWidth () || x == 0) {lastStopX = x; if (mOnEndScrollListener! = null) {mOnEndScrollListener.onScrollEnded (); }}}
Snicolas
Das funktioniert nicht immer. Wie im Emulator wird Public Void Fling (int VelocityY) nicht immer aufgerufen. Der Schwellenwert für das Bildlaufende beträgt manchmal mehr als 20 px.
Andrew Feng
9

Ich habe (horizontal) ScrollView untergeordnet und so etwas gemacht:

@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
    if (Math.abs(x - oldX) > SlowDownThreshold) {  
        currentlyScrolling = true;
    } else {
        currentlyScrolling = false;
        if (!currentlyTouching) {
            //scrolling stopped...handle here
        }
    }
    super.onScrollChanged(x, y, oldX, oldY);
}

Ich habe den Wert 1 für den SlowDownThreshold verwendet, da dies immer der Unterschied zum letzten onScrollChanged-Ereignis zu sein scheint.

Damit sich dies beim langsamen Ziehen richtig verhält, musste ich Folgendes tun:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            currentlyTouching = true;
    }
    return super.onInterceptTouchEvent(event);
}

@Override
public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            currentlyTouching = false;
            if (!currentlyScrolling) {
                //I handle the release from a drag here
                return true;
            }
    }
    return false;
}
Ross Hambrick
quelle
Gute Antwort. Die einzige Ergänzung. In onInterceptTouchEvent () haben Sie auch MotionEvent.ACTION_UP und MotionEvent.ACTION_CANCEL verarbeitet, andernfalls bleibt aktuellTouching wahr, falls Sie eine andere Aktivität beim Klicken öffnen.
Cynichniy Bandera
Noch ein Fix: In onScrollChanged () müssen Sie auch nach dem Ende des Bildlaufs suchen, auch wenn die Verlangsamung noch nicht 1 ist. So: boolean end = (oldx> x)? (x <= 0): (x> = getMaxScrollAmount ()); if (Math.abs (x - oldx)> 1 &&! end) {scroll start; } else {scroll end}
Cynichniy Bandera
Dies funktioniert bei einigen Geräten, z. B. GS3, nicht. Bei meiner GS3 kann der Unterschied zwischen y und altem y in der Mitte des Bildlaufs 1 sein. Es funktioniert jedoch perfekt auf meinem Nexus 4. Ich bin mir nicht sicher warum.
Bryan
Funktioniert auch nicht mit Samsung Galaxy Note 2 und Asus Memo Pad 7. Ich sehe auch Inkremente von 1 in der Mitte des Bildlaufs auf diesen Geräten.
Oliver Hausler
7

Mein Ansatz besteht darin, den Bildlaufstatus durch einen Zeitstempel zu bestimmen, der bei jedem Aufruf von onScrollChanged () geändert wird. Es ist sehr einfach festzustellen, wann der Bildlauf beginnt und endet. Sie können auch den Schwellenwert ändern (ich verwende 100 ms), um die Empfindlichkeit zu korrigieren.

public class CustomScrollView extends ScrollView {
    private long lastScrollUpdate = -1;

    private class ScrollStateHandler implements Runnable {

        @Override
        public void run() {
            long currentTime = System.currentTimeMillis();
            if ((currentTime - lastScrollUpdate) > 100) {
                lastScrollUpdate = -1;
                onScrollEnd();
            } else {
                postDelayed(this, 100);
            }
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (lastScrollUpdate == -1) {
            onScrollStart();
            postDelayed(new ScrollStateHandler(), 100);
        }

        lastScrollUpdate = System.currentTimeMillis();
    }

    private void onScrollStart() {
        // do something
    }

    private void onScrollEnd() {
        // do something
    }

}
Lin Yu Cheng
quelle
Tolle Lösung. Funktioniert wie angenommen.
Oliver Hausler
Danke für deinen Code. So erkennen Sie die Scroll-Start-Listener-Methode anhand Ihres Codes. Bitte schauen Sie meine Frage stackoverflow.com/questions/27864700/…
MAMurali
6

Hier ist noch eine andere Lösung, die meiner Meinung nach recht einfach und sauber ist und natürlich von den obigen Antworten inspiriert wurde. Wenn der Benutzer die Geste beendet hat, prüfen Sie nach einer kurzen Verzögerung (hier 50 ms), ob sich getScrollY () noch ändert.

public class ScrollViewWithOnStopListener extends ScrollView {

    OnScrollStopListener listener;

    public interface OnScrollStopListener {
        void onScrollStopped(int y);
    }

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

    public ScrollViewWithOnStopListener(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                checkIfScrollStopped();
        }

        return super.onTouchEvent(ev);
    }

    int initialY = 0;

    private void checkIfScrollStopped() {
        initialY = getScrollY();
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                int updatedY = getScrollY();
                if (updatedY == initialY) {
                    //we've stopped
                    if (listener != null) {
                        listener.onScrollStopped(getScrollY());
                    }
                } else {
                    initialY = updatedY;
                    checkIfScrollStopped();
                }
            }
        }, 50);
    }

    public void setOnScrollStoppedListener(OnScrollStopListener yListener) {
        listener = yListener;
    }
}
Aleksandarf
quelle
5

Mein Ansatz für diese Frage besteht darin, einen Timer zu verwenden, um nach den folgenden 2 "Ereignissen" zu suchen.

1) onScrollChanged () wurde nicht mehr aufgerufen

2) Der Finger des Benutzers wird aus der Bildlaufansicht angehoben

public class CustomScrollView extends HorizontalScrollView {

public CustomScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

Timer ntimer = new Timer();
MotionEvent event;

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) 
{
    checkAgain();
    super.onScrollChanged(l, t, oldl, oldt);
}

public void checkAgain(){
    try{
        ntimer.cancel();
        ntimer.purge();
    }
    catch(Exception e){}
    ntimer = new Timer();
    ntimer.schedule(new TimerTask() {

        @Override
        public void run() {

            if(event.getAction() == MotionEvent.ACTION_UP){
                // ScrollView Stopped Scrolling and Finger is not on the ScrollView
            }
            else{
                // ScrollView Stopped Scrolling But Finger is still on the ScrollView
                checkAgain();
            }
        }
    },100);

}

@Override
public boolean onTouchEvent(MotionEvent event) {
    this.event = event; 
    return super.onTouchEvent(event);
    }
}
ZeroG
quelle
2

Schauen Sie sich diese Frage hier auf StackOverflow an - sie entspricht nicht genau Ihrer Frage, gibt jedoch eine Vorstellung davon, wie Sie das Bildlaufereignis von a verwalten können ScrollView.

Grundsätzlich müssen Sie Ihre eigenen erstellen, CustomScrollViewindem Sie sie erweitern ScrollViewund überschreiben onScrollChanged(int x, int y, int oldx, int oldy). Dann müssen Sie dies in Ihrer Layout-Datei anstelle des Standard- ScrollViewLike referenzieren com.mypackage.CustomScrollView.

kaspermoerch
quelle
Dieses onScrollChanged () ist genau das, was ich gerade benutze. Trotzdem danke.
user1053691
2

Für einen einfachen Fall, wie Sie ihn beschrieben haben, können Sie wahrscheinlich mit der überschreibenden Schleudermethode in Ihrer benutzerdefinierten Bildlaufansicht davonkommen. Die Fling-Methode wird aufgerufen, um jedes Mal, wenn der Benutzer seinen Finger vom Bildschirm hebt, eine "Verzögerung" durchzuführen.

Was Sie also tun sollten, ist ungefähr so:

  1. Unterklasse ScrollView.

    public class MyScrollView extends ScrollView { 
    
        private Scroller scroller;  
        private Runnable scrollerTask; 
    
        //...
    
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            scroller = new Scroller(getContext()); //or OverScroller for 3.0+
            scrollerTask = new Runnable() {
                @Override
                public void run() {
                    scroller.computeScrollOffset();
                    scrollTo(0, scroller.getCurrY());
    
                    if (!scroller.isFinished()) {
                        MyScrollView.this.post(this);
                    } else {
                        //deceleration ends here, do your code
                    }
                }
            };
            //...
        }
    }
    
  2. Subclass-Fling-Methode und NICHT Superclass-Implementierung aufrufen.

    @Override  
    public void fling(int velocityY) {  
        scroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, container.getHeight());
        post(scrollerTask);  
    
        //add any extra functions you need from android source code:
        //show scroll bars
        //change focus
        //etc.
    }
    
  3. Fling wird nicht ausgelöst, wenn der Benutzer aufhört zu scrollen, bevor er seinen Finger hebt (Geschwindigkeit Y == 0). Wenn Sie diese Art von Ereignissen ebenfalls abfangen möchten, überschreiben Sie onTouchEvent.

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean eventConsumed = super.onTouchEvent(ev);
        if (eventConsumed && ev.getAction() == MotionEvent.ACTION_UP) {
            if (scroller.isFinished()) {
                //do your code
            }
        }
        return eventConsumed;
    }
    

HINWEIS Obwohl dies funktioniert, ist es möglicherweise eine schlechte Idee, die Schleudermethode zu überschreiben. Es ist öffentlich, aber kaum für Unterklassen konzipiert. Im Moment macht es 3 Dinge - es initiiert das Fling für privaten mScroller, behandelt mögliche Fokusänderungen und zeigt Bildlaufleisten an. Dies könnte sich in zukünftigen Android-Versionen ändern. Beispielsweise hat die private mScroller-Instanz ihre Klasse zwischen 2.3 und 3.0 von Scroller in OvershootScroller geändert. Sie müssen all diese kleinen Unterschiede berücksichtigen. Seien Sie auf jeden Fall bereit für unvorhergesehene Folgen in der Zukunft.

Alexey
quelle
Was ist container.getHeight () im zweiten Schritt (2.)?
Mitul Varmora
1
Container ist die Ansicht in scrollView. Da scrollView nur 1 untergeordnetes Element enthalten kann, können Sie den Container durch getChildAt (0) ersetzen.
Alexey
2

Hier ist meine Lösung, die Scroll-Tracking und Scroll-Ending umfasst:

public class ObservableHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollListener {
        public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
        public void onEndScroll(ObservableHorizontalScrollView scrollView);
    }

    private boolean mIsScrolling;
    private boolean mIsTouching;
    private Runnable mScrollingRunnable;
    private OnScrollListener mOnScrollListener;

    public ObservableHorizontalScrollView(Context context) {
        this(context, null, 0);
    }

    public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ObservableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();

        if (action == MotionEvent.ACTION_MOVE) {
            mIsTouching = true;
            mIsScrolling = true;
        } else if (action == MotionEvent.ACTION_UP) {
            if (mIsTouching && !mIsScrolling) {
                if (mOnScrollListener != null) {
                    mOnScrollListener.onEndScroll(this);
                }
            }

            mIsTouching = false;
        }

        return super.onTouchEvent(ev);
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);

        if (Math.abs(oldX - x) > 0) {
            if (mScrollingRunnable != null) {
                removeCallbacks(mScrollingRunnable);
            }

            mScrollingRunnable = new Runnable() {
                public void run() {
                    if (mIsScrolling && !mIsTouching) {
                        if (mOnScrollListener != null) {
                            mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
                        }
                    }

                    mIsScrolling = false;
                    mScrollingRunnable = null;
                }
            };

            postDelayed(mScrollingRunnable, 200);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
        }
    }

    public OnScrollListener getOnScrollListener() {
        return mOnScrollListener;
    }

    public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
        this.mOnScrollListener = mOnEndScrollListener;
    }

}
ZaBlanc
quelle
2

Ich denke, das ist in der Vergangenheit aufgetaucht. AFAIK, das kann man nicht leicht erkennen. Mein Vorschlag ist, dass Sie einen Blick darauf werfen ScrollView.java(so machen wir die Dinge in Android Land :)) und herausfinden, wie Sie die Klasse erweitern können, um die Funktionalität bereitzustellen, die Sie suchen. Folgendes würde ich zuerst versuchen:

 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
     if (mScroller.isFinished()) {
         // do something, for example call a listener
     }
 }
Felix
quelle
Schön, dass ich die isFinished-Methode nicht bemerkt habe!
Jordi Coscolla
3
Vielen Dank. Wie greife ich auf diese isFinished () -Methode zu? Ich habe mir die Datei ScrollView.java angesehen und mScroller ist privat und ich kann nicht sagen, wie ich darauf zugreifen kann.
user1053691
Hmm, in der Tat. Ich habe nicht genau genug hingeschaut. Deshalb habe ich gesagt, ich würde das zuerst versuchen :). Sie könnten andere Dinge tun, wie Spur ruft onScrollChanged, scrollTooder jedes anderes Scroll-verwandtes Verfahren. Alternativ können Sie einfach den gesamten ScrollViewCode kopieren und dieses Feld erstellen protected. Das würde ich allerdings nicht empfehlen.
Felix
1
Zugriff auf mScroller durch Reflexion. Funktioniert auch nicht, isFinished () wird jedes Mal ausgelöst, wenn die Bildlaufansicht Ihre Fingerposition erreicht, was fast kontinuierlich ist. Das Bildlaufstopp-Ereignis sollte ausgelöst werden, wenn beide Benutzer ihren Finger loslassen und der Bildlauf den Bildlauf beendet.
Amik
20
Diese Antwort sah wirklich vielversprechend aus, aber da sie irgendwie in einer Sackgasse liegt, sollte sie vielleicht entfernt werden?
Spaaarky21
2

Meine Lösung ist eine Variation der großartigen Lösung von Lin Yu Cheng und erkennt auch, wann das Scrollen gestartet und gestoppt wurde.

Schritt 1. Definieren Sie eine HorizontalScrollView und einen OnScrollChangedListener:

CustomHorizontalScrollView scrollView = (CustomHorizontalScrollView) findViewById(R.id.horizontalScrollView);

horizontalScrollListener = new CustomHorizontalScrollView.OnScrollChangedListener() {
    @Override
    public void onScrollStart() {
        // Scrolling has started. Insert your code here...
    }

    @Override
    public void onScrollEnd() {
         // Scrolling has stopped. Insert your code here...
    }
};

scrollView.setOnScrollChangedListener(horizontalScrollListener);

Schritt 2. Fügen Sie die CustomHorizontalScrollView-Klasse hinzu:

public class CustomHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollChangedListener {
        // Developer must implement these methods.
        void onScrollStart();
        void onScrollEnd();
    }

    private long lastScrollUpdate = -1;
    private int scrollTaskInterval = 100;
    private Runnable mScrollingRunnable;
    public OnScrollChangedListener mOnScrollListener;

    public CustomHorizontalScrollView(Context context) {
        this(context, null, 0);
        init(context);
    }

    public CustomHorizontalScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init(context);
    }

    public CustomHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        // Check for scrolling every scrollTaskInterval milliseconds
        mScrollingRunnable = new Runnable() {
            public void run() {
                if ((System.currentTimeMillis() - lastScrollUpdate) > scrollTaskInterval) {
                    // Scrolling has stopped.
                    lastScrollUpdate = -1;
                    //CustomHorizontalScrollView.this.onScrollEnd();
                    mOnScrollListener.onScrollEnd();
                } else {
                    // Still scrolling - Check again in scrollTaskInterval milliseconds...
                    postDelayed(this, scrollTaskInterval);
                }
            }
        };
    }

    public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) {
        this.mOnScrollListener = onScrollChangedListener;
    }

    public void setScrollTaskInterval(int scrollTaskInterval) {
        this.scrollTaskInterval = scrollTaskInterval;
    }

    //void onScrollStart() {
    //    System.out.println("Scroll started...");
    //}

    //void onScrollEnd() {
    //    System.out.println("Scroll ended...");
    //}

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (mOnScrollListener != null) {
            if (lastScrollUpdate == -1) {
                //CustomHorizontalScrollView.this.onScrollStart();
                mOnScrollListener.onScrollStart();
                postDelayed(mScrollingRunnable, scrollTaskInterval);
            }

            lastScrollUpdate = System.currentTimeMillis();
        }
    }
}
D. Clarke
quelle
2

Dies ist ein alter Thread, aber ich möchte eine kürzere Lösung hinzufügen, die ich mir ausgedacht habe:

 buttonsScrollView.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->

            handler.removeCallbacksAndMessages(null)

            handler.postDelayed({
                  //YOUR CODE TO BE EXECUTED HERE
                },1000)
        }

Natürlich gibt es eine Verzögerung von 1000 Millisekunden. Passen Sie das an, wenn Sie müssen.

pepinobr
quelle
1

Ich habe die Antwort von ZeroG verbessert. Hauptsächlich das Abbrechen überschüssiger Aufgabenaufrufe und das Implementieren des Ganzen als privater OnTouchListener, sodass sich der gesamte Bildlauferkennungscode an einem Ort befindet.

Fügen Sie den folgenden Code in Ihre eigene ScrollView-Implementierung ein:

private class ScrollFinishHandler implements OnTouchListener
{
        private static final int SCROLL_TASK_INTERVAL = 100;

        private Runnable    mScrollerTask;
        private int         mInitialPosition = 0;

        public ScrollFinishHandler()
        {
            mScrollerTask = new Runnable() {

                public void run() {

                    int newPosition = getScrollY();
                    if(mInitialPosition - newPosition == 0)
                    {//has stopped

                       onScrollStopped(); // Implement this on your main ScrollView class
                    }else{
                        mInitialPosition = getScrollY();
                        ExpandingLinearLayout.this.postDelayed(mScrollerTask, SCROLL_TASK_INTERVAL);
                    }
                }
            };
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) 
        {
            if (event.getAction() == MotionEvent.ACTION_UP) 
            {

                startScrollerTask();
            }
            else
            {
                stopScrollerTask();
            }

            return false;
        }

}

Und dann in Ihrer ScrollView-Implementierung:

setOnTouchListener( new ScrollFinishHandler() );
Vaiden
quelle
1

Hier gibt es einige gute Antworten, aber mein Code kann erkennen, wenn das Scrollen stoppt, ohne die ScrollView-Klasse erweitern zu müssen. Jede Ansichtsinstanz kann getViewTreeObserver () aufrufen. Wenn Sie diese Instanz von ViewTreeObserver halten, können Sie mit der Funktion addOnScrollChangedListener () einen OnScrollChangedListener hinzufügen.

deklarieren Sie Folgendes:

private ScrollView scrollListener;
private volatile long milesec;

private Handler scrollStopDetector;
private Thread scrollcalled = new Thread() {

    @Override
    public void run() { 
        if (System.currentTimeMillis() - milesec > 200) {
            //scroll stopped - put your code here
        }
    }
}; 

und in Ihrem onCreate (oder einem anderen Ort) hinzufügen:

    scrollListener = (ScrollView) findViewById(R.id.scroll);

    scrollListener.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {

        @Override
        public void onScrollChanged() {
            milesec = System.currentTimeMillis();
            scrollStopDetector.postDelayed(scrollcalled, 200);
        }
    });

Möglicherweise möchten Sie zwischen diesen Überprüfungen eine längere oder langsamere Zeit benötigen, aber beim Scrollen wird dieser Listener sehr schnell aufgerufen, sodass er sehr schnell funktioniert.

Zbun
quelle
Ich habe es in diesem Fall nicht versucht, aber meiner Erfahrung nach würde der wiederholte und wechselseitige Aufruf von postDelayed von onScrollChanged zu Speicherverlusten führen.
Oliver Hausler
-1
        this.getListView().setOnScrollListener(new OnScrollListener(){
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {}

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
                     if( firstVisibleItem + visibleItemCount >= totalItemCount ) 
                              // Last item is shown...

            }

Hoffe das Snippet hilft :)

Jordi Coscolla
quelle
Leider verwende ich eine ScrollView, keine ListView. Trotzdem danke.
user1053691
1
Hallo? ScrollView nicht ListView.
Kevin Parker