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ę?

arnp
źródło
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
    }
});
Zbun
źródło
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
        }
    });
Midhun Vijayakumar
źródło
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;
    }

}
ZaBlanc
źródło
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
        }
    }
}
mcspayne
źródło
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")
}
Cristan
źródło
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);
}

}
Chuanxing Zhao
źródło
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---------
Wirote W.
źródło
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