Подтвердить что ты не робот

Обнаружить конец ScrollView

У меня есть приложение, у которого есть Activity, который использует ScrollView. Мне нужно определить, когда пользователь доходит до нижней части ScrollView. Я сделал некоторые googleing, и я нашел эту страницу, где объясняется. Но в этом примере ребята расширяют ScrollView. Как я уже сказал, мне нужно расширить Activity.

Итак, я сказал: "Хорошо, попробуйте сделать собственный класс, расширяющий ScrollView, переопределите метод onScrollChanged(), определите конец прокрутки и действуйте соответственно".

Я сделал, но в этой строке:

scroll = (ScrollViewExt) findViewById(R.id.scrollView1);

он выдает a java.lang.ClassCastException. Я изменил теги <ScrollView> в своем XML, но, очевидно, это не работает. Мои вопросы: почему, если ScrollViewExt extends ScrollView, бросает мне на лицо a ClassCastException? есть ли способ обнаружить конец прокрутки без лишнего шума?

Спасибо, люди.

EDIT: Как и было обещано, вот часть моего XML:

<ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >


        <WebView
            android:id="@+id/textterms"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_horizontal"
            android:textColor="@android:color/black" />

    </ScrollView>

Я изменил его с TextView на WebView, чтобы иметь возможность обосновать текст внутри. То, что я хочу достичь, - кнопка "Принять" не активируется, пока условия контракта не будут полностью прочитаны ". Мой расширенный класс называется ScrollViewEx t. Если я изменяю тег ScrollView для ScrollViewExt, он выдает

android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt

потому что он не понимает тег ScrollViewEx. Я не думаю, что у него есть решение...

Спасибо за ваши ответы!

4b9b3361

Ответ 1

Сделал это!

Помимо исправления Alexandre любезно предоставил мне, мне пришлось создать интерфейс:

public interface ScrollViewListener {
    void onScrollChanged(ScrollViewExt scrollView, 
                         int x, int y, int oldx, int oldy);
}

Затем мне пришлось переопределить метод OnScrollChanged из ScrollView в моем ScrollViewExt:

public class ScrollViewExt extends ScrollView {
    private ScrollViewListener scrollViewListener = null;
    public ScrollViewExt(Context context) {
        super(context);
    }

    public ScrollViewExt(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public ScrollViewExt(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (scrollViewListener != null) {
            scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
        }
    }
}

Теперь, как сказал Alexandre, поместите имя пакета в тег XML (моя ошибка), сделайте мой класс Activity реализованным ранее созданным интерфейсом, а затем соберите все вместе:

scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
scroll.setScrollViewListener(this);

И в методе OnScrollChanged, из интерфейса...

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
    // We take the last son in the scrollview
    View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
    int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

    // if diff is zero, then the bottom has been reached
    if (diff == 0) {
        // do stuff
    }
}

И это сработало!

Большое спасибо за вашу помощь, Александр!

Ответ 2

Я нашел простой способ обнаружить это:

   scrollView.getViewTreeObserver()
       .addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                if (scrollView.getChildAt(0).getBottom()
                     <= (scrollView.getHeight() + scrollView.getScrollY())) {
                    //scroll view is at bottom
                } else {
                    //scroll view is not at bottom
                }
            }
        });
  • Не нужно настраивать ScrollView.
  • Scrollview может содержать только один прямой дочерний элемент, поэтому scrollView.getChildAt(0) можно использовать.
  • Это правильное решение, даже если высота прямого потомка представления прокрутки равна match_parent или wrap_content.

Ответ 3

Все эти ответы настолько сложны, но есть простой встроенный метод, который выполняет это: canScrollVertically(int)

Например:

@Override
public void onScrollChanged() {
    if (!scrollView.canScrollVertically(1)) {
        // bottom of scroll view
    }
    if (!scrollView.canScrollVertically(-1)) {
        // top of scroll view
    }
}

Это также работает с RecyclerView, ListView и фактически любым другим представлением, поскольку метод реализован в View.

Если у вас есть горизонтальный ScrollView, то же самое можно сделать с помощью canScrollHorizontally(int)

Ответ 4

Ответ Fustigador был отличным, но я нашел какое-то устройство (например, Samsung Galaxy Note V) не может достигнуть 0, осталось 2 пункта после расчета. Я предлагаю добавить небольшой буфер, как показано ниже:

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
    // We take the last son in the scrollview
    View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
    int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

    // if diff is zero, then the bottom has been reached
    if (diff <= 10) {
        // do stuff
    }
}

Ответ 5

ИЗМЕНИТЬ

С содержимым вашего XML я вижу, что вы используете ScrollView. Если вы хотите использовать свое собственное представление, вы должны написать com.your.packagename.ScrollViewExt, и вы сможете использовать его в своем коде.

<com.your.packagename.ScrollViewExt
    android:id="@+id/scrollView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <WebView
        android:id="@+id/textterms"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:textColor="@android:color/black" />

</com.your.packagename.ScrollViewExt>

EDIT END

Не могли бы вы разместить содержимое xml?

Я думаю, что вы могли бы просто добавить прослушиватель прокрутки и проверить, является ли последний показанный последним последним из списка:

mListView.setOnScrollListener(new OnScrollListener() {      
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub      
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub
        if(view.getLastVisiblePosition()==(totalItemCount-1)){
            //dosomething
        }
    }
});

Ответ 6

Вы можете использовать интерфейс библиотеки поддержки NestedScrollView и NestedScrollView.OnScrollChangeListener.

https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

В противном случае, если ваше приложение предназначено для API 23 или выше, вы можете использовать следующий метод в ScrollView:

View.setOnScrollChangeListener(OnScrollChangeListener listener) 

Затем следуйте примеру, описанному @Fustigador в его ответе. Обратите внимание, однако, что, как описано в @, вам следует подумать о добавлении небольшого буфера, если пользователь или система не смогут достичь полного дна списка по любой причине.

Также стоит отметить, что прослушиватель смены прокрутки иногда вызывается с отрицательными значениями или значениями, превышающими высоту представления. Предположительно, эти значения представляют собой "импульс" действия прокрутки. Однако, если они не обрабатываются надлежащим образом (пол/абс), они могут вызвать проблемы с обнаружением направления прокрутки при прокрутке представления вверху или внизу диапазона.

https://developer.android.com/reference/android/view/View.html#setOnScrollChangeListener(android.view.View.OnScrollChangeListener)

Ответ 7

Мы всегда должны добавлять scrollView.getPaddingBottom(), чтобы соответствовать полной высоте прокрутки, потому что некоторое время прокрутки имеет прописку в xml файле, так что случай не будет работать.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            if (scrollView != null) {
               View view = scrollView.getChildAt(scrollView.getChildCount()-1);
               int diff = (view.getBottom()+scrollView.getPaddingBottom()-(scrollView.getHeight()+scrollView.getScrollY()));

          // if diff is zero, then the bottom has been reached
               if (diff == 0) {
               // do stuff
                }
            }
        }
    });

Ответ 8

scrollView = (ScrollView) findViewById(R.id.scrollView);

    scrollView.getViewTreeObserver()
            .addOnScrollChangedListener(new 
            ViewTreeObserver.OnScrollChangedListener() {
                @Override
                public void onScrollChanged() {

                    if (!scrollView.canScrollVertically(1)) {
                        // bottom of scroll view
                    }
                    if (!scrollView.canScrollVertically(-1)) {
                        // top of scroll view


                    }
                }
            });

Ответ 9

Большинство ответов работает помимо того факта, что когда вы прокручиваете до конца, слушатель запускается несколько раз, что в моем случае нежелательно. Чтобы избежать этого, я добавил флаг scrollPositionChanged, который проверяет, изменилась ли даже позиция прокрутки перед повторным вызовом метода.

public class EndDetectingScrollView extends ScrollView {
    private boolean scrollPositionChanged = true;

    private ScrollEndingListener scrollEndingListener;

    public interface ScrollEndingListener {
        void onScrolledToEnd();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        View view = this.getChildAt(this.getChildCount() - 1);
        int diff = (view.getBottom() - (this.getHeight() + this.getScrollY()));
        if (diff <= 0) {
            if (scrollPositionChanged) {
                scrollPositionChanged = false;
                if (scrollEndingListener != null) {
                    scrollEndingListener.onScrolledToEnd();
                }
            }
        } else {
            scrollPositionChanged = true;
        }
    }

    public void setScrollEndingListener(ScrollEndingListener scrollEndingListener) {
        this.scrollEndingListener = scrollEndingListener;
    }
}

Тогда просто установите слушателя

scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() {
    @Override
    public void onScrolledToEnd() {
        //do your stuff here    
    }
});

Вы можете сделать то же самое, если вы делаете как

scrollView.getViewTreeObserver().addOnScrollChangedListener(...)

но вы должны предоставить флаг из класса, добавив этот слушатель.

Ответ 10

Чтобы определить, находитесь ли вы в конце пользовательского ScrollView, вы также можете использовать переменную-член и сохранить последнюю позицию y. Затем вы можете сравнить последнюю позицию y с текущей полосой прокрутки.

private int scrollViewPos;

...

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {

   //reached end of scrollview
   if (y > 0 && scrollViewPos == y){
      //do something
   }

   scrollViewPos = y;
}

Ответ 11

Я хотел показать/скрыть FAB со смещением до самого низа прокрутки. Вот решение, которое я придумал (Котлин):

scrollview.viewTreeObserver.addOnScrollChangedListener {
    if (scrollview.scrollY < scrollview.getChildAt(0).bottom - scrollview.height - offset) {
        // fab.hide()
    } else {
        // fab.show()
    }
}