57

在将元素添加到与列表视图关联的数组适配器后,我试图平滑滚动到列表的最后一个元素。问题是它只是滚动到一个随机位置

arrayadapter.add(item);
//DOES NOT WORK CORRECTLY:
listview.smoothScrollToPosition(arrayadapter.getCount()-1);

//WORKS JUST FINE:
listview.setSelection(arrayadapter.getCount()-1);
4

9 回答 9

64

您可能想告诉 ListView 在 UI 线程可以处理它时发布滚动(这就是您的滚动不正确的原因)。SmoothScroll 需要做很多工作,而不是仅仅去一个忽略速度/时间/等的位置。(对于“动画”是必需的)。

因此,您应该执行以下操作:

    getListView().post(new Runnable() {
        @Override
        public void run() {
            getListView().smoothScrollToPosition(pos);
        }
    });
于 2013-08-08T18:23:10.483 回答
15

(Copied from my answer: smoothScrollToPositionFromTop() is not always working like it should)

This is a known bug. See https://code.google.com/p/android/issues/detail?id=36062

However, I implemented this workaround that deals with all edge cases that might occur:

First call smothScrollToPositionFromTop(position) and then, when scrolling has finished, call setSelection(position). The latter call corrects the incomplete scrolling by jumping directly to the desired position. Doing so the user still has the impression that it is being animation-scrolled to this position.

I implemented this workaround within two helper methods:

smoothScrollToPosition()

public static void smoothScrollToPosition(final AbsListView view, final int position) {
    View child = getChildAtPosition(view, position);
    // There's no need to scroll if child is already at top or view is already scrolled to its end
    if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {
        return;
    }

    view.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(final AbsListView view, final int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                view.setOnScrollListener(null);

                // Fix for scrolling bug
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        view.setSelection(position);
                    }
                });
            }
        }

        @Override
        public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
                                 final int totalItemCount) { }
    });

    // Perform scrolling to position
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            view.smoothScrollToPositionFromTop(position, 0);
        }
    });
}

getChildAtPosition()

public static View getChildAtPosition(final AdapterView view, final int position) {
    final int index = position - view.getFirstVisiblePosition();
    if ((index >= 0) && (index < view.getChildCount())) {
        return view.getChildAt(index);
    } else {
        return null;
    }
}
于 2014-08-13T12:48:08.890 回答
7

您应该使用 setSelection() 方法。

于 2012-09-27T07:48:19.140 回答
5

使用 LayoutManager 平滑滚动

layoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);
于 2019-01-09T19:24:37.863 回答
4
final Handler handler = new Handler();
//100ms wait to scroll to item after applying changes
handler.postDelayed(new Runnable() {
@Override
public void run() {
   listView.smoothScrollToPosition(selectedPosition);
}}, 100);
于 2016-05-27T20:38:12.493 回答
3

Lars 提到的集合选择方法有效,但动画对于我们的目的来说太跳跃了,因为它跳过了剩下的任何内容。另一种解决方案是重复调用该方法,直到第一个可见位置是您的索引。最好快速完成并且有限制,否则它将与用户滚动视图发生冲突。

private  void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) {
    if (listView.FirstVisiblePosition != index && attempts < 10) {
        attempts++;
        listView.SmoothScrollToPositionFromTop (index, 1, 100);
        listView.PostDelayed (() => {
            DeterminedScrollTo (listView, index, attempts);
        }, 100);
    }
}

解决方案是在 C# via 中。Xamarin 但应该很容易翻译成 Java。

于 2015-09-17T10:26:20.503 回答
1

你打电话arrayadapter.notifyDataSetChanged()后打电话arrayadapter.add()吗?也可以肯定的是,smoothScrollToPosition这些setSelection方法ListView不是arrayadapter你上面提到的。

无论如何,看看这是否有帮助: notifyDataSetChanged not working in android after smoothScrollToPosition

于 2012-07-11T12:05:46.427 回答
0

在android java中使用它,它对我有用:

private void DeterminedScrollTo(int index, int attempts) {
        if (listView.getFirstVisiblePosition() != index && attempts < 10) {
            attempts++;
            if (listView.canScrollVertically(pos))
                listView.smoothScrollToPositionFromTop(index, 1, 200);
            int finalAttempts = attempts;
            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    DeterminedScrollTo(index, finalAttempts);
                }
            }, 200);
        }
    }
于 2021-04-12T20:05:30.717 回答
0

kotlin 中的解决方案:

fun AbsListView.scrollToIndex(index: Int, duration: Int = 150) {
    smoothScrollToPositionFromTop(index, 0, duration)
    postDelayed({
        setSelection(index)
        post { smoothScrollToPositionFromTop(index, 0, duration) }
    }, duration.toLong())
}

PS:看起来它在 Android SDK 方面很混乱,所以如果你不想手动计算视图偏移量,这是你能得到的最好的方法。也许最好的简单方法是将长列表的持续时间设置为 0 以避免任何可见的跳转。

在 GridView 的某些位置调用 setSelection 时我遇到了一些问题,所以在我看来,这确实是解决方案,而不是使用它。

于 2021-09-08T02:55:18.703 回答