Чи можу я мати OnScrollListener для ScrollView?


140

Я використовую HorizontalScrollViewмакет у, і мені потрібно встановити, чи користувач досяг початкової та кінцевої точки прокрутки.

Бо ListViewя спробував a, onScrollListenerі можна знайти початкову та кінцеву точку прокрутки.

Я намагався зробити те ж саме в своєму, Scrollviewале це здається не можливим. Чи є інші можливі способи досягти того, що мені потрібно.


3
Можливо. Дивіться відповідь користувача2695685 Коротше кажучи, наступний текст onStartзробить трюк: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});в onStart (), де hsvє HorizontalScrollViewтвір.
JohnnyLambada

прийміть будь-яку корисну відповідь ... якщо інше опублікуйте свою власну відповідь ..
Ранджіт Кумар

12
Чому виявити подію прокрутки за допомогою ScrollView в Android так складно? Це горіхи imo.
працював

Відповіді:


389

Кожен екземпляр перегляду викликів getViewTreeObserver(). Тепер, тримаючи примірник ViewTreeObserver, ви можете додати OnScrollChangedListener()до нього за допомогою методу addOnScrollChangedListener().

Більше інформації про цей клас можна переглянути тут .

Це дозволяє вам бути в курсі кожного прокручування події, але без координат. Отримати їх можна, користуючись getScrollY()або getScrollX()слухачем.

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
Цю відповідь слід позначити правильною. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});в onStart (), де hsvє HorizontalScrollViewтвори. Я підозрюю, що те саме буде працювати і для ScrollView.
JohnnyLambada

3
точно. це найкраща відповідь. не потрібно розширювати HorizontalScrollView. Щойно перевірена на ScrollView, чудово працює: остаточний ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (новий ViewTreeObserver.OnScrollChangedListener () {@Override public void onScrollChanged () {Log.d ("onScrollChanged Y", String.valueOY (sv.getSc)
Рафаель C

5
Спершу слід перевірити ViewTreeObserver.isAlive ()
M.Salomaa

7
Зразок коду у відповіді вносить витік пам'яті. Оскільки цього методу є addі немає set, всі слухачі будуть зберігатися до явного видалення. Таким чином, анонімний клас, який використовується як реалізація слухача у зразку, просочиться (разом із усім, на що він посилається, тобто із зовнішнім класом).
Brett Duncavage

7
Напевно, тупе питання, але що таке rootScrollView?
Ікбал

49

Це може бути дуже корисно. Використовуйте NestedScrollViewзамість ScrollView. Підтримка Бібліотека 23,1 представила OnScrollChangeListenerв NestedScrollView. Тож ви можете зробити щось подібне.

 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
Однозначно простіше, ніж відстежувати, чи раніше ви додавали слухача за допомогою getViewTreeObserver (). Дякую!
Ентоні Міллс

Але як використовувати NestedScrollView в якості горизонтального перегляду прокрутки? Не можете знайти жоден ресурс
GensaGames

Що робити, якщо я хочу використовувати горизонтальну прокрутку?
Микита Аксіонов

7
@ dazed'n'confused NestedScrollViewє частиною бібліотеки підтримки, її setOnScrollChangeListenerметод не вимагає мінімальної версії.
Том

4
Не плутати з View's, для setOnScrollChangeListenerяких потрібен рівень 23 API
Anders Ullnæss

18

Ось похідний HorizontalScrollView, який я написав для обробки повідомлень про прокрутку та закінчення прокрутки. Він належним чином обробляє, коли користувач перестав активно прокручувати та коли він повністю сповільнюється після того, як користувач відпустить:

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
Схоже, це краще, ніж використовувати метод ViewTreeObserver. Просто тому, що у вас є діяльність, де у вас завантажено кілька фрагментів (наприклад, 3 фрагменти із розсувними вкладками) з ListViews та ScrollViews, і вам потрібно зареєструвати події для конкретного перегляду. Якщо, якщо ViewTreeObserver зареєстрований, він запускатиме події, навіть якщо це не активний перегляд.
Torsten Ojaperv

3
@ZaBlanc - Чи можете ви, будь ласка, розкажіть мені, як ініціалізувати цей клас та слухачів з моєї діяльності, яка містить ScrollView? Я реалізував це просто чудово, але слухачі ще не повернуть жодних значень.
Едмонд Тамас

Це насправді працює! і немає необхідності в sdk> 23 для цього :)
Matan Dahan

5

Якщо ви хочете знати положення прокрутки подання, ви можете використовувати наступну функцію розширення для класу 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

Ви можете використовувати NestedScrollViewзамість ScrollView. Однак, використовуючи Kotlin Lambda, він не буде знати, що ви хочете NestedScrollView setOnScrollChangeListenerзамість того, який знаходиться в View (який є рівнем API 23). Ви можете виправити це, вказавши перший параметр як NestedScrollView.

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

Це має бути прийнятою відповіддю, оскільки метод, що використовує viewTreeObserver, спричиняє протікання пам’яті та повинен бути видалений вручну, інакше буде додано кілька спостерігачів прокрутки.
Рей Лі

1

ви можете визначити спеціальний клас ScrollView і додати інтерфейс, який буде викликаний при прокрутці так:

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

будь ласка, спробуйте додати деякий опис, щоб підтримати ваш код, який дозволить усім зрозуміти тісто
Aniruddha Das

Цей код дуже схожий на іншу відповідь , чи не так?
Сергій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.