Tôi có thể có onScrollListener cho ScrollView không?


140

Tôi đang sử dụng một HorizontalScrollViewbố cục và tôi cần xác định người dùng đã đạt đến điểm bắt đầu và điểm kết thúc của cuộn.

ListViewtôi đã thử onScrollListenervà có thể tìm thấy điểm bắt đầu và điểm kết thúc của cuộn.

Tôi đã cố gắng làm điều tương tự trong tôi Scrollviewnhưng dường như không thể. Có cách nào khác có thể để đạt được những gì tôi cần.


3
Điều đó là có thể. Xem câu trả lời của người dùng2695685. Nói tóm lại, phần sau đây onStartsẽ thực hiện thủ thuật: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});trong onStart () hsvlà một HorizontalScrollViewtác phẩm.
JohnnyLambada

chấp nhận bất cứ ai câu trả lời hữu ích..nếu người khác đăng câu trả lời của riêng bạn ..
Ranjith Kumar

12
Tại sao việc phát hiện sự kiện cuộn với ScrollView lại khó khăn như vậy trong Android? Đây là hạt imo.
làm việc

Câu trả lời:


389

Mỗi phiên bản của cuộc gọi Xem getViewTreeObserver(). Bây giờ khi giữ một thể hiện của ViewTreeObserver, bạn có thể thêm OnScrollChangedListener()nó vào phương thức này addOnScrollChangedListener().

Bạn có thể xem thêm thông tin về lớp học này ở đây .

Nó cho phép bạn nhận thức được mọi sự kiện cuộn - nhưng không có tọa độ. Bạn có thể có được chúng bằng cách sử dụng getScrollY()hoặc getScrollX()từ bên trong người nghe.

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
Câu trả lời này cần được đánh dấu chính xác. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});trong onStart () nơi hsvlà một HorizontalScrollViewtác phẩm. Tôi nghi ngờ điều tương tự cũng sẽ làm việc cho một ScrollView.
JohnnyLambada

3
chính xác. đây là câu trả lời tốt nhất không cần phải mở rộng ngang dọc. Chỉ cần thử nghiệm với ScrollView, hoạt động rất tốt: cuối cùng ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (new ViewTreeObserver.OnScrollChangedListener () {@Override void công khai onScrollChanged () {Log.d ("onScrollChanged Y", String.value ())
Raphael C

5
Trước tiên, bạn nên kiểm tra ViewTreeObserver.isAlive ()
M.Salomaa

6
Mã mẫu trong câu trả lời giới thiệu rò rỉ bộ nhớ. Vì phương thức này là addvà không set, tất cả người nghe sẽ được giữ lại cho đến khi loại bỏ rõ ràng. Vì vậy, lớp ẩn danh được sử dụng làm triển khai trình nghe trong mẫu sẽ bị rò rỉ (cùng với bất kỳ thứ gì nó đang tham chiếu, tức là lớp bên ngoài).
Brett Duncavage

7
Có lẽ là một câu hỏi ngớ ngẩn, nhưng rootScrollView là gì?
Iqbal

49

Điều này có thể rất hữu ích. Sử dụng NestedScrollViewthay vì ScrollView. Hỗ trợ Thư viện 23.1 giới thiệu một OnScrollChangeListenerđể NestedScrollView. Vì vậy, bạn có thể làm một cái gì đó như thế này.

 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
Chắc chắn dễ dàng hơn việc theo dõi xem bạn đã thêm người nghe trước đây với getViewTreeObserver () chưa. Cảm ơn!
Anthony Mills

Nhưng làm thế nào để sử dụng NestedScrollView làm chế độ xem cuộn ngang? Không thể tìm thấy bất kỳ tài nguyên nào
GensaGames

Nếu tôi muốn sử dụng cuộn ngang thì sao?
Nikita Axyonov

7
@ dazed'n' bối rối NestedScrollViewlà một phần của thư viện hỗ trợ, setOnScrollChangeListenerphương thức của nó không có yêu cầu phiên bản tối thiểu.
Tom

4
Không nên nhầm với View's setOnScrollChangeListenermà đòi hỏi mức API 23
Anders Ullnæss

18

Đây là một màn hình ngang có nguồn gốc mà tôi đã viết để xử lý các thông báo về việc cuộn và kết thúc cuộn. Nó xử lý đúng cách khi người dùng đã ngừng chủ động cuộn khi nó giảm tốc hoàn toàn sau khi người dùng cho phép:

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
Có vẻ như điều này tốt hơn so với sử dụng phương pháp ViewTreeObserver. Chỉ vì khi bạn có một hoạt động trong đó bạn có nhiều đoạn được tải (ví dụ 3 đoạn có tab trượt) với ListViews và ScrollViews và bạn cần đăng ký các sự kiện để xem cụ thể. Trong khi nếu ViewTreeObserver được đăng ký, nó sẽ kích hoạt các sự kiện ngay cả khi nó không hoạt động.
Torsten Ojaperv

3
@ZaBlanc - Bạn có thể vui lòng gọi cho tôi cách khởi tạo lớp này và người nghe từ Hoạt động của tôi, tổ chức ScrollView không? Tôi đã thực hiện nó tốt, nhưng người nghe sẽ không trả lại bất kỳ giá trị nào.
Edmond Tamas

Điều này thực sự hoạt động! và không cần sdk> 23 cho nó :)
Matan Dahan

5

Nếu bạn muốn biết vị trí cuộn của một khung nhìn, thì bạn có thể sử dụng chức năng mở rộng sau trên lớp 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

Bạn có thể sử dụng NestedScrollViewthay vì ScrollView. Tuy nhiên, khi sử dụng Kotlin Lambda, sẽ không biết bạn muốn NestedScrollView setOnScrollChangeListenerthay vì ở chế độ Xem (đó là API cấp 23). Bạn có thể khắc phục điều này bằng cách chỉ định tham số đầu tiên là NestedScrollView.

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

Đây phải là câu trả lời được chấp nhận vì phương thức sử dụng viewTreeObserver gây rò rỉ bộ nhớ và phải được xóa thủ công nếu không sẽ có nhiều trình quan sát cuộn khác được thêm vào.
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---------

vui lòng thử thêm một số mô tả để hỗ trợ mã của bạn, điều này sẽ khiến mọi người hiểu về người đánh bóng
Aniruddha Das

Mã này rất giống với câu trả lời khác , phải không?
Sergii

1

bạn có thể định nghĩa một lớp ScrollView tùy chỉnh và thêm một giao diện được gọi khi cuộn như thế này:

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

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.