Android: Phát hiện khi ScrollView dừng cuộn


84

Tôi đang sử dụng một ScrollViewtrong Android và trong đó phần hiển thị của ScrollViewnó có cùng kích thước với một trong các ô bên trong Scrollview. Mọi "ô" đều có cùng chiều cao. Vì vậy, những gì tôi đang cố gắng làm là cố định vị trí sau khi ScrollViewđã được cuộn.

Hiện tại, tôi đang phát hiện khi nào người dùng đã chạm vào ScrollViewvà khi nào họ bắt đầu cuộn và xử lý nó từ đó, nhưng nó khá lỗi. Nó cũng cần hoạt động khi người dùng chỉ cần chạm nhẹ và nó sẽ cuộn và sau đó giảm tốc độ.

Trên iPhone, có một chức năng giống như vậy didDeceleratevà ở đó tôi có thể thực hiện bất kỳ mã nào tôi muốn khi quá trình ScrollViewcuộn hoàn tất. Có một điều như vậy với Android? Hoặc có một số mã tôi có thể xem xét để tìm ra cách tốt hơn để làm điều đó?

Tôi đã xem qua tài liệu Android và không thể tìm thấy bất kỳ thứ gì tương tự.

Câu trả lời:


101

Gần đây tôi đã phải triển khai chức năng mà bạn đã mô tả. Những gì tôi đã làm là kiểm tra Runnable nếu ScrollView đã ngừng cuộn bằng cách so sánh giá trị được trả về bởi getScrollY () khi onTouchEvent được kích hoạt lần đầu với giá trị được trả về sau một thời gian được xác định bởi biến newCheck .

Xem mã bên dưới (giải pháp hoạt động):

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

}

Sau đó, tôi có:

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

        }
});

BTW Tôi đã sử dụng một vài ý tưởng từ các câu trả lời khác để thực hiện việc này trong ứng dụng của mình. Hi vọng điêu nay co ich. Bất kỳ câu hỏi cảm thấy tự do để hỏi. Chúc mừng.


11
Cảm ơn, đã tìm kiếm độ tuổi cho một cái gì đó như thế này. Thay vào đó, được sử dụng cho HorizontalScrollView, (rõ ràng) cũng hoạt động. Chỉ getScrollY () phải được thay thế bằng getScrollX ()
Schnodahipfe

1
Đã thử tất cả ở đây. Đây là cái DUY NHẤT hoạt động.
Mike6679

23

Đây là một bản sửa lỗi khác cho lỗi sự kiện OnEndScroll thiếu IMHO trong ScrollView.

Nó lấy cảm hứng từ câu trả lời hambonious . Chỉ cần thả lớp này vào dự án của bạn (thay đổi gói để phù hợp với gói của riêng bạn) và sử dụng xml bên dưới

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

}

lại thay đổi tên gói để phù hợp với dự án của bạn

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

2
+1 Câu trả lời hay, đơn giản. Tôi thay đổi giá trị chênh lệch 2để 0.5fđể phản ánh bản gốc ScrollViews' MAX_SCROLL_FACTORgiá trị.
Phil

2
Việc triển khai này rất hay nhưng không thành công khi chế độ xem cuộn không được lấp đầy khi bắt đầu. Đây là một giải pháp thay thế (đối với chế độ xem cuộn ngang, vì vậy hãy thay thế tất cả x bằng y để có được một hình dọc): @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

Điều này không phải lúc nào cũng hoạt động. Như trong trình giả lập, không phải lúc nào cũng gọi được public void fling (int speedY). Ngưỡng kết thúc cuộn đôi khi lớn hơn 20 px.
Andrew Feng

9

Tôi đã phân lớp con ScrollView (Ngang) và làm một việc như sau:

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

Tôi đã sử dụng giá trị 1 cho SlowDownThreshold vì nó dường như luôn là sự khác biệt của sự kiện onScrollChanged cuối cùng.

Để làm cho điều này hoạt động chính xác khi kéo chậm, tôi phải làm điều này:

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

Câu trả lời hay đấy. Sự bổ sung duy nhất. Trong onInterceptTouchEvent (), bạn cũng phải xử lý MotionEvent.ACTION_UP và MotionEvent.ACTION_CANCEL, nếu không thì hiện tạiTouch sẽ vẫn đúng trong trường hợp bạn mở một hoạt động khác khi nhấp chuột.
Cynichniy Bandera

Một cách khắc phục nữa: trong onScrollChanged (), bạn cũng phải kiểm tra xem có kết thúc cuộn hay không ngay cả khi tốc độ chậm chưa 1. Như thế này: boolean end = (oldx> x)? (x <= 0): (x> = getMaxScrollAmount ()); if (Math.abs (x - oldx)> 1 &&! end) {scroll start; } else {scroll end}
Cynichniy Bandera

Điều này không hoạt động trên một số thiết bị, tức là GS3. Trên GS3 của tôi, sự khác biệt giữa y và y cũ CÓ THỂ là 1 ở giữa cuộn. Tuy nhiên, nó hoạt động hoàn hảo trên Nexus 4 của tôi. Tôi cung không chăc tại sao.
Bryan

Không hoạt động trên Samsung Galaxy Note 2 và Asus Memo Pad 7. Tôi cũng thấy số gia tăng 1 ở giữa cuộn trên các thiết bị này.
Oliver Hausler

7

Cách tiếp cận của tôi là xác định trạng thái cuộn bằng một dấu thời gian được thay đổi mỗi khi onScrollChanged () được gọi. Rất dễ dàng để xác định thời điểm bắt đầu và kết thúc cuộn. Bạn cũng có thể thay đổi ngưỡng (tôi sử dụng 100ms) để sửa độ nhạy.

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
    }

}

Giải pháp tuyệt vời. Hoạt động như giả định.
Oliver Hausler

Cảm ơn vì mã của bạn. Cách phát hiện phương pháp nghe bắt đầu cuộn từ mã của bạn. vui lòng xem câu hỏi của tôi stackoverflow.com/questions/27864700/…
MAMurali

6

Đây là một giải pháp khác, khá đơn giản và sạch sẽ theo quan điểm của tôi, được truyền cảm hứng tự nhiên từ các câu trả lời ở trên. Về cơ bản khi người dùng kết thúc kiểm tra cử chỉ nếu getScrollY () vẫn đang thay đổi, sau một khoảng thời gian ngắn (ở đây là 50ms).

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

5

Cách tiếp cận của tôi cho câu hỏi này là sử dụng bộ đếm thời gian để kiểm tra 2 "sự kiện" sau.

1) onScrollChanged () ngừng được gọi

2) Ngón tay của người dùng được nhấc lên khỏi chế độ xem cuộn

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

3

Giải pháp của tôi là một biến thể của giải pháp tuyệt vời của Lin Yu Cheng và cũng phát hiện thời điểm bắt đầu và dừng cuộn.

Bước 1. Xác định HorizontalScrollView và 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);

Bước 2. Thêm lớp 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();
        }
    }
}

2

Hãy thử xem câu hỏi này tại đây trên StackOverflow - nó không hoàn toàn giống với câu hỏi của bạn, nhưng nó đưa ra ý tưởng về cách bạn có thể quản lý sự kiện cuộn của mộtScrollView .

Về cơ bản, bạn cần tạo của riêng mình CustomScrollViewbằng cách mở rộng ScrollViewvà ghi đè onScrollChanged(int x, int y, int oldx, int oldy). Sau đó, bạn cần tham chiếu điều này trong tệp bố cục của mình thay vì tiêu chuẩn ScrollViewnhư com.mypackage.CustomScrollView.


OnScrollChanged () này thực sự là những gì tôi đang sử dụng vào lúc này. Xin cảm ơn.
user1053691

2

Đối với trường hợp đơn giản như bạn mô tả, bạn có thể sử dụng phương pháp ghép nối ghi đè trong chế độ xem cuộn tùy chỉnh của mình. Phương thức Fling được gọi để thực hiện "giảm tốc" mỗi khi người dùng đưa ngón tay lên khỏi màn hình.

Vì vậy, những gì bạn nên làm là một cái gì đó như sau:

  1. ScrollView lớp con.

    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. Phương thức chia lớp con và KHÔNG gọi hiện thực lớp cha.

    @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. Fling sẽ không kích hoạt nếu người dùng ngừng cuộn trước khi giơ ngón tay lên (vận tốcY == 0). Trong trường hợp bạn cũng muốn chặn loại sự kiện này, hãy ghi đè 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;
    }
    

LƯU Ý Mặc dù cách này có hiệu quả, nhưng phương pháp ghép dòng ghi đè có thể là một ý tưởng tồi. Nó là công khai, nhưng nó hầu như không được thiết kế để phân lớp con. Ngay bây giờ, nó thực hiện 3 việc - nó bắt đầu fling cho mScroller riêng tư, xử lý các thay đổi tiêu điểm có thể có và hiển thị các thanh cuộn. Điều này có thể thay đổi trong bản phát hành Android trong tương lai. Ví dụ: cá thể mScroller riêng đã thay đổi lớp của nó từ Scroller thành OvershootScroller giữa 2.3 và 3.0. Bạn phải ghi nhớ tất cả những khác biệt nhỏ này. Trong mọi trường hợp, hãy sẵn sàng cho những hậu quả không lường trước được trong tương lai.


Containerner.getHeight () trong bước thứ hai (2.) là gì?
Mitul Varmora,

1
vùng chứa là dạng xem bên trong scrollView. Vì scrollView chỉ có thể chứa 1 con, bạn có thể thay thế vùng chứa bằng getChildAt (0).
Alexey

2

Có một số câu trả lời tuyệt vời ở đây, nhưng mã của tôi có thể phát hiện khi cuộn dừng mà không cần phải mở rộng lớp ScrollView. mọi cá thể dạng xem đều có thể gọi getViewTreeObserver (). khi Giữ phiên bản ViewTreeObserver này, bạn có thể thêm OnScrollChangedListener bằng cách sử dụng hàm addOnScrollChangedListener ().

khai báo như sau:

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

và trong onCreate của bạn (hoặc một nơi khác), hãy thêm:

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

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

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

bạn có thể muốn mất thời gian lâu hơn hoặc chậm hơn giữa các lần kiểm tra này, nhưng khi cuộn, trình liệt kê này được gọi rất nhanh nên nó sẽ hoạt động rất nhanh.


Tôi chưa thử trong trường hợp này, nhưng từ kinh nghiệm của mình, tôi đoán cuộc gọi lặp đi lặp lại và đối ứng của postDelayed từ onScrollChanged sẽ gây rò rỉ bộ nhớ.
Oliver Hausler

2

Đây là giải pháp của tôi bao gồm theo dõi cuộn và kết thúc cuộn:

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

}

2

Tôi nghĩ điều này đã xảy ra trong quá khứ. AFAIK, bạn không thể dễ dàng phát hiện ra điều đó. Đề xuất của tôi là bạn hãy xem ScrollView.java(đó là cách chúng tôi thực hiện mọi thứ trên đất Android :)) và tìm ra cách bạn có thể mở rộng lớp để cung cấp chức năng bạn đang tìm kiếm. Đây là những gì tôi sẽ thử đầu tiên:

 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
     if (mScroller.isFinished()) {
         // do something, for example call a listener
     }
 }

Rất vui, tôi đã không nhận thấy phương pháp isFinishing!
Jordi Coscolla

3
Cảm ơn. Làm cách nào để truy cập vào phương thức isFinishing () này? Tôi đã xem qua tệp ScrollView.java và mScroller là riêng tư và tôi không thể biết cách nào để có quyền truy cập vào nó.
user1053691

Thật vậy. Tôi đã không nhìn đủ gần. Đó là lý do tại sao tôi đã nói rằng tôi sẽ thử điều đó đầu tiên :). Bạn có thể làm những việc khác, như theo dõi các cuộc gọi đến onScrollChanged, scrollTohoặc bất kỳ phương pháp cuộn liên quan khác. Ngoài ra, bạn có thể sao chép toàn bộ ScrollViewmã và tạo trường đó protected. Tuy nhiên, tôi sẽ không khuyên bạn điều đó.
Felix

1
Đã truy cập mScroller thông qua phản chiếu. Cũng không hoạt động, isFinishing () được kích hoạt mỗi khi chế độ xem cuộn đến vị trí ngón tay của bạn, điều này gần như liên tục. Sự kiện dừng cuộn sẽ kích hoạt khi cả người dùng thả ngón tay và cuộn dừng cuộn.
amik

20
Câu trả lời này trông thực sự hứa hẹn nhưng vì nó gần như đã kết thúc, có lẽ nó nên bị loại bỏ?
spaaarky21

2

đây là một chủ đề cũ nhưng tôi muốn thêm một giải pháp ngắn hơn mà tôi đã nghĩ ra:

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

            handler.removeCallbacksAndMessages(null)

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

Đương nhiên có độ trễ 1000 mili giây. Điều chỉnh nếu bạn cần.


1

Tôi đã thực hiện một số cải tiến cho câu trả lời của ZeroG. Chủ yếu là hủy các lệnh gọi tác vụ dư thừa và triển khai toàn bộ dưới dạng OnTouchListener riêng tư, vì vậy tất cả mã phát hiện cuộn sẽ ở một nơi.

Dán mã sau vào triển khai ScrollView của riêng bạn:

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

}

Và sau đó trong triển khai ScrollView của bạn:

setOnTouchListener( new ScrollFinishHandler() );

-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...

            }

Hy vọng đoạn mã giúp đỡ :)


Rất tiếc, tôi đang sử dụng ScrollView, không phải ListView. Xin cảm ơn.
user1053691

1
Xin chào? ScrollView không phải ListView.
Kevin Parker
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.