Обнаружить конец 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
. Я не думаю, что у него есть решение...
Спасибо за ваши ответы!
Ответы
Ответ 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()
}
}