Czy mogę mieć onScrollListener dla ScrollView?


140

Używam HorizontalScrollVieww układzie i muszę zidentyfikować użytkownika, który osiągnął punkt początkowy i końcowy przewijania.

Ponieważ ListViewpróbowałem onScrollListeneri można znaleźć początek i koniec przewijania.

Próbowałem zrobić to samo w moim, Scrollviewale wydaje się to niemożliwe. Czy są inne możliwe sposoby osiągnięcia tego, czego potrzebuję?


3
To jest możliwe. Zobacz odpowiedź użytkownika2695685. W skrócie o następujących onStartrade: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});w onStart () gdzie hsvjest to HorizontalScrollViewprace.
— JohnnyLambada

zaakceptuj każdą użyteczną odpowiedź… jeśli jeszcze napiszesz własną odpowiedź…
— Ranjith Kumar

12
Dlaczego wykrywanie zdarzenia przewijania za pomocą ScrollView jest tak trudne w systemie Android? To jest orzechy imo.
— pracował

Odpowiedzi:


389

Każde wystąpienie wywołania View getViewTreeObserver(). Teraz, gdy trzymasz instancję ViewTreeObserver, możesz dodać OnScrollChangedListener()do niej za pomocą metody addOnScrollChangedListener().

Więcej informacji o tych zajęciach znajdziesz tutaj .

Pozwala być świadomym każdego zdarzenia przewijania - ale bez współrzędnych. Możesz je jednak uzyskać, używając getScrollY()lub getScrollX()z poziomu słuchacza.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        int scrollY = rootScrollView.getScrollY(); // For ScrollView
        int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
        // DO SOMETHING WITH THE SCROLL COORDINATES
    }
});

6
Tę odpowiedź należy zaznaczyć jako poprawną. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});w onStart () gdzie hsvjest to HorizontalScrollViewprace. Podejrzewam, że to samo zadziała również dla ScrollView.
— JohnnyLambada

3
dokładnie. to najlepsza odpowiedź. nie ma potrzeby rozszerzania HorizontalScrollView. Właśnie przetestowany z ScrollView, działa świetnie: final ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (new ViewTreeObserver.OnScrollChangedListener () {@Override public void onScrollChanged () {Log.d ("onScrollChanged Y", String.valueOf (sv.getScroll);
— Raphael C,

5
Powinieneś najpierw sprawdzić ViewTreeObserver.isAlive ()
— M.Salomaa

7
Przykładowy kod w odpowiedzi wprowadza przeciek pamięci. Ponieważ metoda jest addi nie set, wszystkie detektory zostaną zachowane, dopóki nie zostaną jawnie usunięte. Więc anonimowa klasa użyta jako implementacja nasłuchiwania w przykładzie wycieknie (wraz z wszystkim, do czego się odwołuje, tj. Klasą zewnętrzną).
— Brett Duncavage

7
Prawdopodobnie głupie pytanie, ale co to jest rootScrollView?
— Iqbal

49

To może być bardzo przydatne. Użyj NestedScrollViewzamiast ScrollView. Support Library 23.1 wprowadził OnScrollChangeListenerdo NestedScrollView. Możesz więc zrobić coś takiego.

 myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
            Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
            //Do something
        }
    });

2
Zdecydowanie łatwiejsze niż śledzenie, czy wcześniej dodałeś detektor za pomocą metody getViewTreeObserver (). Dzięki!
— Anthony Mills

Ale jak używać NestedScrollView jako widoku przewijania poziomego? Nie mogę znaleźć żadnego zasobu
— GensaGames

A jeśli chcę używać przewijania w poziomie?
— Nikita Axyonov

7
@ dazed'n'confused NestedScrollViewjest częścią biblioteki wsparcia, jego setOnScrollChangeListenermetoda nie wymaga minimalnej wersji.
— Tom,

4
Nie należy mylić z View„s setOnScrollChangeListener, która wymaga poziom API 23
— Anders Ullnæss

18

Oto pochodny HorizontalScrollView, który napisałem do obsługi powiadomień o przewijaniu i kończeniu przewijania. Prawidłowo radzi sobie z sytuacjami, gdy użytkownik przestał aktywnie przewijać i gdy całkowicie zwalnia po puszczeniu przez użytkownika:

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

}

1
Wygląda na to, że jest to lepsze niż użycie metody ViewTreeObserver. Tylko dlatego, że gdy masz aktywność, w której masz załadowanych wiele fragmentów (na przykład 3 fragmenty z przesuwanymi zakładkami) z ListViews i ScrollViews i musisz zarejestrować zdarzenia dla określonego widoku. Natomiast jeśli ViewTreeObserver jest zarejestrowany, będzie uruchamiał zdarzenia, nawet jeśli nie jest to aktywny widok.
— Torsten Ojaperv

3
@ZaBlanc - Czy możesz mi powiedzieć, jak zainicjować tę klasę i słuchaczy z mojej aktywności, która zawiera ScrollView? Zaimplementowałem to dobrze, ale słuchacze nie zwracają jeszcze żadnych wartości.
— Edmond Tamas,

To faktycznie działa! i nie potrzeba do tego sdk> 23 :)
— Matan Dahan

5

Jeśli chcesz poznać pozycję przewijania widoku, możesz użyć następującej funkcji rozszerzenia w klasie View:

fun View?.onScroll(callback: (x: Int, y: Int) -> Unit) {
    var oldX = 0
    var oldY = 0
    this?.viewTreeObserver?.addOnScrollChangedListener {
        if (oldX != scrollX || oldY != scrollY) {
            callback(scrollX, scrollY)
            oldX = scrollX
            oldY = scrollY
        }
    }
}

5

Możesz użyć NestedScrollViewzamiast ScrollView. Jednak gdy używasz Kotlin Lambda, nie będzie wiedział, że chcesz NestedScrollView setOnScrollChangeListenerzamiast tego w widoku (który jest na poziomie API 23). Możesz to naprawić, określając pierwszy parametr jako NestedScrollView.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}

Powinna to być akceptowana odpowiedź, ponieważ metoda wykorzystująca viewTreeObserver powoduje wycieki pamięci i musi zostać ręcznie usunięta, w przeciwnym razie zostanie dodanych wiele obserwatorów przewijania.
— Ray Li

1

możesz zdefiniować niestandardową klasę ScrollView i dodać interfejs, który będzie wywoływany podczas przewijania w następujący sposób:

public class ScrollChangeListenerScrollView extends HorizontalScrollView {


private MyScrollListener mMyScrollListener;

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

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

public ScrollChangeListenerScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


public void setOnMyScrollListener(MyScrollListener myScrollListener){
    this.mMyScrollListener = myScrollListener;
}


@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if(mMyScrollListener!=null){
        mMyScrollListener.onScrollChange(this,l,t,oldl,oldt);
    }

}

public interface MyScrollListener {
    void onScrollChange(View view,int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}

}

0
    // --------Start Scroll Bar Slide--------
    final HorizontalScrollView xHorizontalScrollViewHeader = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewHeader);
    final HorizontalScrollView xHorizontalScrollViewData = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewData);
    xHorizontalScrollViewData.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            int scrollX; int scrollY;
            scrollX=xHorizontalScrollViewData.getScrollX();
            scrollY=xHorizontalScrollViewData.getScrollY();
            xHorizontalScrollViewHeader.scrollTo(scrollX, scrollY);
        }
    });
    // ---------End Scroll Bar Slide---------

spróbuj dodać opis, aby wesprzeć twój kod, który sprawi, że wszyscy zrozumieją ciasto
— Aniruddha Das

Ten kod jest bardzo podobny do innej odpowiedzi , prawda?
— Sergii
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.