Android: Wykryj, kiedy ScrollView przestaje przewijać

84

Używam ScrollVieww Androidzie i gdzie widoczna część ScrollViewma taki sam rozmiar jak jedna z komórek wewnątrz Scrollview. Każda „komórka” ma taką samą wysokość. Więc to, co próbuję zrobić, to przyciągnąć do pozycji po ScrollViewprzewinięciu.

Obecnie wykrywam, kiedy użytkownik dotknął przycisku ScrollViewi kiedy zaczął go przewijać i opracowywać, ale jest dość błędny. Musi również działać, gdy użytkownik po prostu go klika, a następnie przewija, a następnie zwalnia.

Na iPhonie jest funkcja, która jest podobna didDeceleratei mogę zrobić dowolny kod, kiedy ScrollViewskończy przewijanie. Czy jest coś takiego z Androidem? A może jest jakiś kod, na który mógłbym spojrzeć, aby znaleźć lepszy sposób na zrobienie tego?

Przejrzałem dokumentację Androida i nie mogłem znaleźć czegoś takiego.

user1053691
źródło

Odpowiedzi:

101

Niedawno musiałem zaimplementować opisaną przez Ciebie funkcję. To, co zrobiłem, to sprawdzenie Runnable, czy ScrollView przestał przewijać, porównując wartość zwróconą przez getScrollY (), gdy onTouchEvent jest po raz pierwszy wyzwalany z wartością zwracaną po czasie zdefiniowanym przez zmienną newCheck .

Zobacz kod poniżej (działające rozwiązanie):

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

}

Wtedy ja mam:

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

        }
});

Przy okazji, wykorzystałem kilka pomysłów z innych odpowiedzi, aby to zrobić w mojej aplikacji. Mam nadzieję że to pomoże. Wszelkie pytania zachęcamy do zadawania. Twoje zdrowie.

tulio84z
źródło
11
Dzięki, od lat szukałem czegoś takiego. Zamiast tego jest używany w widoku HorizontalScrollView, który (oczywiście) również działa. Tylko getScrollY () musi zostać zastąpione przez getScrollX ()
Schnodahipfe Kwietnia
1
Wypróbowałem je wszystkie tutaj. To JEDYNY, który działa.
Mike6679
23

Oto kolejna poprawka IMHO, brakujący błąd zdarzenia OnEndScroll w ScrollView.

Inspiruje go hałaśliwa odpowiedź. Po prostu upuść tę klasę do swojego projektu (zmień pakiet na swój własny) i użyj poniższego XML

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

}

ponownie zmieniając nazwę pakietu, aby pasowała do twojego projektu

<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>
pojęcie
źródło
2
+1 Ładna, prosta odpowiedź. Zmienić wartość różnicy 2aby 0.5fodzwierciedlać oryginał ScrollView„s MAX_SCROLL_FACTORwartość.
Phil,
2
Ta implementacja jest fajna, ale zawiodła, gdy widok przewijania nie jest wypełniony na początku. Oto obejście (dla widoku przewijania poziomego, więc zamień wszystkie x na y, aby uzyskać pionowe): @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
To nie zawsze działa. Podobnie jak w emulatorze, publiczne void fling (int velocityY) nie zawsze jest wywoływane. Próg zakończenia przewijania czasami przekracza 20 pikseli.
Andrew Feng
9

Podklasowałem (Horizontal) ScrollView i zrobiłem coś takiego:

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

Użyłem wartości 1 dla SlowDownThreshold, ponieważ zawsze wydaje się, że jest to różnica ostatniego zdarzenia onScrollChanged.

Aby to zachowywało się poprawnie podczas powolnego przeciągania, musiałem zrobić to:

@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
źródło
Niezła odpowiedź. Jedyny dodatek. W onInterceptTouchEvent () obsługujesz również MotionEvent.ACTION_UP i MotionEvent.ACTION_CANCEL, w przeciwnym razie currentTouching pozostanie prawdziwe w przypadku, gdy otworzysz inną czynność po kliknięciu.
Cynichniy Bandera
Jeszcze jedna poprawka: w onScrollChanged () musisz także sprawdzić koniec przewijania, nawet jeśli spowolnienie jeszcze nie nastąpiło 1. Na przykład: boolean end = (oldx> x)? (x <= 0): (x> = getMaxScrollAmount ()); if (Math.abs (x - oldx)> 1 &&! end) {scroll start; } else {koniec przewijania}
Cynichniy Bandera
To nie działa na niektórych urządzeniach, np. GS3. W moim GS3 różnica między y a starym y MOŻE wynosić 1 w środku zwoju. Działa jednak idealnie na moim Nexusie 4. Nie wiem dlaczego.
Bryan,
Nie działa również na Samsung Galaxy Note 2 i Asus Memo Pad 7. Na tych urządzeniach widzę też przyrosty o 1 w środku przewijania.
Oliver Hausler
7

Moje podejście polega na określeniu stanu przewijania za pomocą znacznika czasu zmienianego za każdym razem, gdy wywoływana jest metoda onScrollChanged (). Bardzo łatwo jest określić, kiedy zaczyna się i kończy przewijanie. Możesz także zmienić próg (używam 100 ms), aby naprawić czułość.

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
źródło
Świetne rozwiązanie. Działa zgodnie z oczekiwaniami.
Oliver Hausler
Dziękuję za twój kod. Jak wykryć metodę odbiornika startu przewijania w kodzie. proszę spojrzeć na moje pytanie stackoverflow.com/questions/27864700/…
MAMurali
6

Oto kolejne rozwiązanie, moim zdaniem dość proste i przejrzyste, naturalnie inspirowane powyższymi odpowiedziami. Zasadniczo po zakończeniu gestu użytkownika sprawdź, czy getScrollY () nadal się zmienia, po krótkim opóźnieniu (tutaj 50 ms).

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
źródło
5

Moje podejście do tego pytania polega na użyciu licznika czasu, aby sprawdzić następujące 2 „zdarzenia”.

1) onScrollChanged () przestał być wywoływany

2) Palec użytkownika zostaje podniesiony z widoku przewijania

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
źródło
3

Moje rozwiązanie jest odmianą świetnego rozwiązania Lin Yu Chenga i wykrywa również rozpoczęcie i zatrzymanie przewijania.

Krok 1. Zdefiniuj HorizontalScrollView i 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);

Krok 2. Dodaj klasę CustomHorizontalScrollView:

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
źródło
2

Spróbuj rzucić okiem na to pytanie w StackOverflow - nie jest dokładnie takie samo jak twoje pytanie, ale daje wyobrażenie o tym, jak możesz zarządzać zdarzeniem przewijania pliku ScrollView.

Po prostu musisz stworzyć własny CustomScrollView, rozszerzając ScrollViewi zastępując onScrollChanged(int x, int y, int oldx, int oldy). Następnie musisz odwołać się do tego w swoim pliku układu zamiast do standardowego, ScrollViewtakiego jak com.mypackage.CustomScrollView.

kaspermoerch
źródło
To onScrollChanged () jest właściwie tym, czego używam w tej chwili. W każdym razie dzięki.
user1053691
2

W przypadku prostego przypadku, takiego jak opisany, prawdopodobnie możesz uciec z nadpisaniem metody rzucania w niestandardowym widoku przewijania. Metoda Fling jest wywoływana w celu wykonania „spowolnienia” za każdym razem, gdy użytkownik podniesie palec z ekranu.

Więc co powinieneś zrobić, to coś takiego:

  1. Podklasa 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. Podklasy odrzucania metody i NIE wywołuj implementacji nadklasy.

    @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. Rzut nie zostanie uruchomiony, jeśli użytkownik przestanie przewijać przed podniesieniem palca (prędkość Y == 0). Jeśli chcesz przechwytywać również tego rodzaju zdarzenia, zastąp 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;
    }
    

UWAGA Chociaż to działa, nadpisywanie metody rzutowania może być złym pomysłem. Jest publiczny, ale ledwo jest przeznaczony do tworzenia podklas. W tej chwili robi 3 rzeczy - inicjuje rzut do prywatnego mScrollera, obsługuje ewentualne zmiany fokusa i pokazuje paski przewijania. Może się to zmienić w przyszłej wersji Androida. Na przykład prywatna instancja mScroller zmieniła swoją klasę z Scroller na OvershootScroller z 2.3 do 3.0. Musisz pamiętać o tych wszystkich małych różnicach. W każdym razie bądź przygotowany na nieprzewidziane konsekwencje w przyszłości.

Alexey
źródło
Co to jest container.getHeight () w drugim kroku (2.)?
Mitul Varmora
1
container to widok znajdujący się w scrollView. Ponieważ scrollView może zawierać tylko 1 element podrzędny, możesz zamienić kontener na getChildAt (0).
Alexey
2

Jest tutaj kilka świetnych odpowiedzi, ale mój kod może wykryć zatrzymanie przewijania bez konieczności rozszerzania klasy ScrollView. każda instancja widoku może wywołać metodę getViewTreeObserver (). Trzymając to wystąpienie ViewTreeObserver, możesz dodać OnScrollChangedListener za pomocą funkcji addOnScrollChangedListener ().

oświadczam, co następuje:

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

aw swoim onCreate (lub innym miejscu) dodaj:

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

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

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

możesz potrzebować dłuższego lub wolniejszego czasu między tymi sprawdzeniami, ale podczas przewijania ten listner jest wywoływany bardzo szybko, więc będzie działał bardzo szybko.

Zbun
źródło
Nie próbowałem w tym przypadku, ale z mojego doświadczenia domyślam się, że powtarzające się i wzajemne wywołanie postDelayed z onScrollChanged spowoduje wycieki pamięci.
Oliver Hausler
2

Oto moje rozwiązanie, które obejmuje śledzenie przewijania i zakończenie przewijania:

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
źródło
2

Myślę, że to wydarzyło się w przeszłości. AFAIK, nie możesz tego łatwo wykryć. Moja sugestia jest taka, abyś przyjrzał się temu ScrollView.java(w ten sposób robimy rzeczy w Androidzie :)) i wymyślił, jak możesz rozszerzyć klasę, aby zapewnić funkcjonalność, której szukasz. Oto, czego spróbuję najpierw:

 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
     if (mScroller.isFinished()) {
         // do something, for example call a listener
     }
 }
Felix
źródło
Fajnie, nie zauważyłem metody isFinished!
Jordi Coscolla
3
Dzięki. Jak uzyskać dostęp do tej metody isFinished ()? Przyjrzałem się plikowi ScrollView.java i mScroller jest prywatny i nie widzę sposobu, aby uzyskać do niego dostęp.
user1053691
Hmm, rzeczywiście. Nie spojrzałem wystarczająco blisko. Dlatego powiedziałem, że spróbuję najpierw :). Możesz robić inne rzeczy, na przykład śledzić połączeniaonScrollChanged , scrollTolub jakakolwiek inna metoda przewijania związane. Alternatywnie możesz po prostu skopiować cały ScrollViewkod i utworzyć to pole protected. Nie polecałbym tego jednak.
Felix
1
Dostęp do mScroller poprzez refleksję. Nie działa również, funkcja isFinished () jest uruchamiana za każdym razem, gdy widok przewijania osiągnie pozycję palca, co jest prawie ciągłe. Zdarzenie zatrzymania przewijania powinno zostać uruchomione, gdy zarówno użytkownik zwolni palec, a rolka przestanie przewijać.
amik
20
Ta odpowiedź wyglądała naprawdę obiecująco, ale skoro nie ma końca, może powinna zostać usunięta?
spaaarky21
2

to jest stary wątek, ale chciałbym dodać krótsze rozwiązanie, które wymyśliłem:

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

            handler.removeCallbacksAndMessages(null)

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

Naturalnie występuje opóźnienie 1000 milisekund. Dostosuj to, jeśli potrzebujesz.

pepinobr
źródło
1

Wprowadziłem kilka ulepszeń w odpowiedzi ZeroG. Głównie anulowanie nadmiernych wywołań zadań i zaimplementowanie całości jako prywatnego OnTouchListener, aby cały kod wykrywania przewijania był w jednym miejscu.

Wklej następujący kod do własnej implementacji ScrollView:

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

}

A następnie w Twojej implementacji ScrollView:

setOnTouchListener( new ScrollFinishHandler() );
Vaiden
źródło
-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...

            }

Mam nadzieję, że fragment pomoże :)

Jordi Coscolla
źródło
Niestety używam ScrollView, a nie ListView. W każdym razie dzięki.
user1053691
1
Witaj? ScrollView nie ListView.
Kevin Parker