Недавно мне пришлось реализовать описанную вами функцию. Что я сделал, так это чтобы Runnable проверял, прекратил ли ScrollView прокрутку, сравнивая значение, возвращаемое getScrollY (), когда onTouchEvent впервые запускается со значением, возвращаемым через время, определенное переменной newCheck .
См. Код ниже (рабочее решение):
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){
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);
}
}
Тогда у меня есть:
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");
}
});
Кстати, я использовал несколько идей из других ответов, чтобы сделать это в своем приложении. Надеюсь это поможет. Любые вопросы, не стесняйтесь спрашивать. Ура.
Вот еще одно исправление, IMHO, ошибки события OnEndScroll в ScrollView.
Его вдохновил достойный ответ. Просто поместите этот класс в свой проект (измените пакет, чтобы он соответствовал вашему собственному) и используйте приведенный ниже xml
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; } }
снова изменив имя пакета, чтобы оно соответствовало вашему проекту
<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
чтобы0.5f
отразить оригинальныйScrollView
«sMAX_SCROLL_FACTOR
значение.Я создал подкласс (Horizontal) ScrollView и сделал что-то вроде этого:
@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); }
Я использовал значение 1 для SlowDownThreshold, так как он всегда кажется разницей последнего события onScrollChanged.
Чтобы заставить это вести себя правильно при медленном перетаскивании, мне пришлось сделать следующее:
@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; }
источник
Мой подход заключается в определении состояния прокрутки по временной метке, изменяемой каждый раз при вызове onScrollChanged (). Определить, когда начинается и заканчивается прокрутка, очень просто. Вы также можете изменить порог (я использую 100 мс), чтобы исправить чувствительность.
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 } }
источник
Вот еще одно решение, на мой взгляд, довольно простое и чистое, естественно вдохновленное ответами выше. Обычно после того, как пользователь завершил проверку жестов, после небольшой задержки (здесь 50 мсек) getScrollY () все еще изменяется.
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; } }
источник
Мой подход к этому вопросу - использовать таймер для проверки следующих 2 «событий».
1) onScrollChanged () перестал вызываться
2) Пользователь поднимает палец от прокрутки
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); } }
источник
В таком простом случае, как вы описали, вы, вероятно, сможете обойтись без переопределения метода fling в своем настраиваемом режиме прокрутки. Метод Fling вызывается для выполнения «замедления» каждый раз, когда пользователь поднимает палец от экрана.
Итак, что вам следует сделать, это примерно так:
Подкласс ScrollView.
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 } } }; //... } }
Subclass fling и НЕ вызывайте реализацию суперкласса.
@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. }
Fling не сработает, если пользователь прекратит прокрутку до того, как поднял палец (velocityY == 0). Если вы хотите также перехватывать подобные события, переопределите 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; }
ПРИМЕЧАНИЕ. Хотя это работает, переопределение метода бросания может быть плохой идеей. Он общедоступный, но не предназначен для создания подклассов. Прямо сейчас он делает 3 вещи - он инициирует бросок для частного mScroller, обрабатывает возможные изменения фокуса и показывает полосы прокрутки. Это может измениться в будущем выпуске Android. Например, частный экземпляр mScroller изменил свой класс с Scroller на OvershootScroller между 2.3 и 3.0. Вы должны иметь в виду все эти небольшие отличия. В любом случае будьте готовы к непредвиденным последствиям в будущем.
источник
Мое решение - это вариант отличного решения Лин Ю Ченг, которое также определяет, когда прокрутка началась и остановилась.
Шаг 1. Определите HorizontalScrollView и 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);
Шаг 2. Добавьте класс 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(); } } }
источник
Попробуйте взглянуть на этот вопрос здесь, в StackOverflow - это не совсем то же самое, что и ваш вопрос, но дает представление о том, как вы можете управлять событием прокрутки файла
ScrollView
.В основном вам нужно создать свой собственный
CustomScrollView
, расширивScrollView
и переопределивonScrollChanged(int x, int y, int oldx, int oldy)
. Затем вам нужно указать это в своем файле макета вместо стандартного,ScrollView
напримерcom.mypackage.CustomScrollView
.источник
Здесь есть отличные ответы, но мой код может определять, когда прокрутка останавливается, без необходимости расширения класса ScrollView. каждый экземпляр представления может вызывать getViewTreeObserver (). удерживая этот экземпляр ViewTreeObserver, вы можете добавить OnScrollChangedListener с помощью функции addOnScrollChangedListener ().
заявляем следующее:
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 } } };
и в вашем onCreate (или другом месте) добавьте:
scrollListener = (ScrollView) findViewById(R.id.scroll); scrollListener.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() { @Override public void onScrollChanged() { milesec = System.currentTimeMillis(); scrollStopDetector.postDelayed(scrollcalled, 200); } });
вам может потребоваться больше или меньше времени между этими проверками, но при прокрутке этот список вызывается очень быстро, поэтому он будет работать очень быстро.
источник
Вот мое решение, которое включает отслеживание прокрутки и окончание прокрутки:
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; } }
источник
Я думаю, что это возникало в прошлом. AFAIK, вы не можете легко это обнаружить. Я предлагаю вам взглянуть
ScrollView.java
(именно так мы и поступаем в мире Android :)) и выяснить, как вы можете расширить класс, чтобы обеспечить нужную вам функциональность. Вот что я бы попробовал в первую очередь:@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { if (mScroller.isFinished()) { // do something, for example call a listener } }
источник
onScrollChanged
,scrollTo
или любой другой способ прокрутки связанной. В качестве альтернативы вы можете просто скопировать весьScrollView
код и создать это полеprotected
. Хотя я бы не рекомендовал это.это старый поток, но я хотел бы добавить более короткое решение, которое я придумал:
buttonsScrollView.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> handler.removeCallbacksAndMessages(null) handler.postDelayed({ //YOUR CODE TO BE EXECUTED HERE },1000) }
Естественно есть задержка в 1000 миллисекунд. Отрегулируйте это, если вам нужно.
источник
Я внес некоторые улучшения в ответ ZeroG. В основном отмена лишних вызовов задач и реализация всего этого как частного OnTouchListener, поэтому весь код обнаружения прокрутки будет в одном месте.
Вставьте следующий код в свою собственную реализацию ScrollView:
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; } }
А затем в вашей реализации ScrollView:
setOnTouchListener( new ScrollFinishHandler() );
источник
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... }
Надеюсь, что фрагмент поможет :)
источник