停止滚动视图在滚动中间。

15

我有一个内容很多的滚动视图。当用户进行 fling 或向下滚动时,我希望滚动视图停在特定的视图位置,其中我正在进行一些动画,然后用户可以再次 fling 或向下滚动。

我尝试了禁用 scrollview,如此处所述,但它只在 scrollview 完全停止时才禁用,并且不能在 fling 的中途停止。

是否有任何方法可以在达到某个视图位置或特定y值时停止滚动视图的 fling?


你尝试过延迟加载吗? - CRUSADER
这种应用行为很可能会破坏用户体验,您确定要实现它吗? - Egor
6个回答

16

我曾经遇到过同样的问题,我的解决方案是:

listView.smoothScrollBy(0,0)

这将停止滚动。


非常好用。我的补充是 - 如果有人尝试使用scrollTo(0, 0)来重置scrollview的位置,例如当scrollview处于惯性(fling)阶段时,它不起作用(scrollTo)。因此解决方案是调用smoothScrollTo(0, 0); - luky
根据情况,scrollBy(0,0)也可以起作用。 - Adil Aliyev
完美 - 这也消除了用户生成的惯性! - Starwave

8

要在特定点停止 fling,只需调用

fling(0)

如果您只关心短暂的操作,我认为这是最合理的方法,因为velosityY被设置为0,从而立即停止了短暂操作。
以下是fling方法的javadoc:
/**
 * Fling the scroll view
 *
 * @param velocityY The initial velocity in the Y direction. Positive
 *                  numbers mean that the finger/cursor is moving down the screen,
 *                  which means we want to scroll towards the top.
 */

在这种情况下,getScrollX()的值将是你想要的其他值。它会产生副作用。 - Adil Aliyev
只是想补充一下,在你滚动到正确的位置之后,应该将这个放在后面。 - Uriel Frankel
只是想补充一下,在你滚动到正确的位置之后,你应该把这个放在后面。 - undefined

4
一种方法是使用smoothScrollToPosition,它可以停止任何现有的滚动运动。请注意,此方法需要API级别>= 8(Android 2.2,Froyo)。
请注意,如果当前位置远离所需位置,则平滑滚动将需要相当长的时间,并且看起来有点卡顿(至少在我的Android 4.4 KitKat测试中是这样)。我还发现,在调用setSelection和smoothScrollToPosition组合时,有时会导致位置稍稍“偏移”,这似乎只在当前位置非常接近所需位置时发生。
在我的情况下,当用户按下按钮时,我希望我的列表跳转到顶部(position=0)(这与您的用例略有不同,因此您需要根据自己的需求进行调整)。
我使用以下方法:
private void smartScrollToPosition(ListView listView, int desiredPosition) {
    // If we are far away from the desired position, jump closer and then smooth scroll
    // Note: we implement this ourselves because smoothScrollToPositionFromTop
    // requires API 11, and it is slow and janky if the scroll distance is large,
    // and smoothScrollToPosition takes too long if the scroll distance is large.
    // Jumping close and scrolling the remaining distance gives a good compromise.
    int currentPosition = listView.getFirstVisiblePosition();
    int maxScrollDistance = 10;
    if (currentPosition - desiredPosition >= maxScrollDistance) {
        listView.setSelection(desiredPosition + maxScrollDistance);
    } else if (desiredPosition - currentPosition >= maxScrollDistance) {
        listView.setSelection(desiredPosition - maxScrollDistance);
    }
    listView.smoothScrollToPosition(desiredPosition); // requires API 8
}

在我的按钮操作处理程序中,我按以下方式调用了它:
    case R.id.action_go_to_today:
        ListView listView = (ListView) findViewById(R.id.lessonsListView);
        smartScrollToPosition(listView, 0); // scroll to top
        return true;

以上内容并没有直接回答你的问题,但如果你能检测到当前位置是否在或接近你想要的位置,则可以使用 smoothScrollToPosition 停止滚动。


虽然它并没有直接回答问题,但这正是我正在寻找的解决方案。谢谢! - akent
这真的会停止任何现有的滚动吗?例如,如果我快速滑动ScrollView,然后调用smoothScrollTo,那么在onScrollChanged回调中,快速滑动似乎仍在继续。 - Micky
我是通过一个按钮触发的(跳转到列表中的特定位置),它确实为我停止了滚动。 例如,如果我快速滑动列表,在它滚动时按下按钮,它会跳转并平稳地滚动到我想要的位置,并且滚动会停止(快速滑动不再有效)。 我根本没有使用onScrollChanged。我只是使用标准的ListAdapter,不需要自己处理快速滑动。 - avparker

1
我正在尝试实现类似的情况。我有一个部分解决方案:
通过重写 onScrollChanged 方法并调用 smoothScrollTo 方法,我已经成功停止了 ScrollView 在特定的 y 坐标处滚动。我通过保留一个布尔值来指示这是否是第一次 fling/drag,从而允许用户继续向下滚动超过该点。同样,当从底部向上滚动时,我再次在相同的点停止滚动。然后再次允许用户向上滚动。
代码看起来像这样:
boolean beenThereFromTop = false;
boolean beenThereFromBottom = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

    scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {

        @Override
        public void onScrollChanged() {

            TextView whereToStop = (TextView) findViewById(R.id.whereToStop);
            final int y = whereToStop.getBottom();

            int scrollY = scrollView.getScrollY();

// manage scrolling from top to bottom

            if (scrollY > y) {
                if (!beenThereFromTop) {
                    beenThereFromTop = true;
                    scrollView.post(new Runnable() {
                        @Override
                        public void run() {
                            scrollView.smoothScrollTo(0, y);
                        }
                    });
                }
            }
            if (scrollY < y && beenThereFromTop) {
                beenThereFromTop = false;
            }

// manage scrolling from bottom to top

            if (scrollY < y) {
                if (!beenThereFromBottom) {
                    beenThereFromBottom = true;
                    scrollView.post(new Runnable() {
                        @Override
                        public void run() {
                            scrollView.smoothScrollTo(0, y);
                        }
                    });
                }
            }
            if (scrollY > y && beenThereFromBottom) {
                beenThereFromBottom = false;
            }
        }
    });
}

1
你需要禁用 ScrollView 处理 fling 操作。只需重写 ScrollView 中的 fling 方法并注释掉 super.fling() 即可。如果这样做有效,请告诉我!
public class CustomScrollView extends ScrollView {

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
    {
    return false;
    }

    @Override
    public void fling (int velocityY)
    {
    /*Scroll view is no longer gonna handle scroll velocity.
    * super.fling(velocityY);
     */
    }
 }

但这样做不会永久禁用 fling 吗?我希望在滚动视图滚到特定的 y 值时停止 fling。 - Deepak Senapati

0

我实际上正在使用这种方法:

 list.post(new Runnable() 
                        {
                            @Override
                            public void run() 
                            {
                               list.smoothScrollToPositionFromTop(arg2, 150); 
                            }
                        });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接