Bisakah saya memiliki onScrollListener untuk ScrollView?

140

Saya menggunakan HorizontalScrollViewdalam tata letak dan saya perlu mengidentifikasi pengguna telah mencapai titik awal dan akhir dari gulir.

Karena ListViewsaya telah mencoba onScrollListenerdan mungkin untuk menemukan titik awal dan akhir gulir.

Saya mencoba melakukan hal yang sama Scrollviewtetapi saya tampaknya tidak mungkin. Apakah ada cara lain yang mungkin untuk mencapai apa yang saya butuhkan.

arnp
sumber
3
Itu mungkin. Lihat jawaban user2695685. Singkatnya, berikut ini onStartakan melakukan trik: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});di onStart () di mana hsvadalah HorizontalScrollViewkarya.
JohnnyLambada
terima siapa pun jawaban yang berguna..jika ada yang mengirim jawaban Anda sendiri ..
Ranjith Kumar
12
Mengapa mendeteksi acara gulir dengan ScrollView begitu sulit di Android? Ini gila kacang.
bekerja

Jawaban:

389

Setiap instance dari View calls getViewTreeObserver(). Sekarang saat memegang instance ViewTreeObserver, Anda dapat menambahkannya OnScrollChangedListener()menggunakan metode addOnScrollChangedListener().

Anda dapat melihat informasi lebih lanjut tentang kelas ini di sini .

Ini memungkinkan Anda mengetahui setiap acara gulir - tetapi tanpa koordinat. Anda bisa mendapatkannya dengan menggunakan getScrollY()atau getScrollX()dari dalam pendengar.

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
sumber
6
Jawaban ini harus ditandai dengan benar. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});di onStart () di mana hsvada HorizontalScrollViewkarya. Saya menduga hal yang sama akan bekerja untuk ScrollView juga.
JohnnyLambada
3
persis. ini jawaban terbaik tidak perlu memperpanjang HorizontalScrollView. Baru saja diuji dengan ScrollView, berfungsi dengan baik: final ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener (baru) {@Override public void onScrollChanged () {Log.d ("onScrollChanged Y", String.valueOf (sv.getalue););
Raphael C
5
Pertama-tama Anda harus memeriksa ViewTreeObserver.isAlive ()
M.Salomaa
6
Kode sampel dalam jawaban menyebabkan kebocoran memori. Karena metode ini adalah adddan tidak set, semua pendengar akan dipertahankan hingga dihapus secara eksplisit. Jadi kelas anonim yang digunakan sebagai implementasi pendengar dalam sampel akan bocor (bersama dengan apa pun itu referensi, yaitu kelas luar).
Brett Duncavage
7
Mungkin pertanyaan bodoh, tapi apa itu rootScrollView?
Iqbal
49

Ini mungkin sangat berguna. Gunakan NestedScrollViewsebagai ganti ScrollView. Perpustakaan Dukungan 23.1 memperkenalkan sebuah OnScrollChangeListenerke NestedScrollView. Jadi Anda bisa melakukan hal seperti ini.

 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
sumber
2
Jelas lebih mudah daripada melacak apakah Anda telah menambahkan pendengar sebelumnya dengan getViewTreeObserver (). Terima kasih!
Anthony Mills
Tetapi bagaimana menggunakan NestedScrollView sebagai tampilan gulir horizontal? Tidak dapat menemukan sumber daya apa pun
GensaGames
Bagaimana jika saya ingin menggunakan gulir horizontal?
Nikita Axyonov
7
@ dazed'n'confused NestedScrollViewadalah bagian dari pustaka dukungan, setOnScrollChangeListenermetodenya tidak memiliki persyaratan versi minimum.
Tom
4
Tidak menjadi bingung dengan View's setOnScrollChangeListeneryang membutuhkan tingkat API 23
Anders Ullnæss
18

Ini adalah HorizontalScrollView turunan yang saya tulis untuk menangani notifikasi tentang pengguliran dan gulir yang berakhir. Itu benar menangani ketika pengguna telah berhenti gulir aktif dan ketika itu sepenuhnya melambat setelah pengguna melepaskan:

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
sumber
1
Sepertinya ini lebih baik daripada menggunakan metode ViewTreeObserver. Hanya karena ketika Anda memiliki aktivitas di mana Anda memiliki beberapa fragmen dimuat (misalnya 3 fragmen dengan tab geser) dengan ListViews dan ScrollViews dan Anda perlu mendaftarkan acara untuk tampilan tertentu. Sedangkan jika ViewTreeObserver terdaftar, ia akan memecat acara bahkan jika itu bukan tampilan aktif.
Torsten Ojaperv
3
@ZaBlanc - Bisakah Anda memberi tahu saya cara menginisialisasi kelas ini dan pendengar dari Aktivitas saya, yang memegang ScrollView? Saya sudah menerapkannya dengan baik, tetapi pendengar belum mengembalikan nilai apa pun.
Edmond Tamas
Ini benar-benar berfungsi! dan tidak perlu untuk SDK> 23 untuk itu :)
Matan Dahan
5

Jika Anda ingin mengetahui posisi gulir tampilan, maka Anda dapat menggunakan fungsi ekstensi berikut pada kelas tampilan:

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
sumber
5

Anda bisa menggunakan NestedScrollViewbukan ScrollView. Namun, ketika menggunakan Kotlin Lambda, ia tidak akan tahu Anda menginginkan NestedScrollView setOnScrollChangeListenerdaripada yang ada di View (yang merupakan level API 23). Anda dapat memperbaikinya dengan menetapkan parameter pertama sebagai NestedScrollView.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
Cristan
sumber
Ini harus menjadi jawaban yang diterima karena metode menggunakan viewTreeObserver menyebabkan kebocoran memori dan harus dihapus secara manual atau beberapa pengamat gulir akan ditambahkan.
Ray Li
1
    // --------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.
sumber
tolong coba tambahkan beberapa deskripsi untuk mendukung kode Anda yang akan membuat semua orang mengerti adonan
Aniruddha Das
Kode ini sangat mirip dengan jawaban lain , bukan?
Sergii
1

Anda dapat menentukan kelas ScrollView khusus, & menambahkan antarmuka dipanggil saat menggulir seperti ini:

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
sumber