android: smoothScrollToPosition() работает неправильно

Я пытаюсь плавно перейти к последнему элементу списка после добавления элемента в адаптер массива, связанный с представлением списка. Проблема в том, что он просто прокручивается до случайной позиции

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

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

person user1515520    schedule 11.07.2012    source источник
comment
и этот метод никогда не работал для меня.   -  person Mohammad Ersan    schedule 11.07.2012
comment
Известная ошибка: см. ответ stackoverflow.com/questions/14479078/   -  person Lars Blumberg    schedule 22.08.2014


Ответы (7)


Вы, вероятно, хотите указать ListView опубликовать прокрутку, когда поток пользовательского интерфейса может ее обработать (поэтому у вас она не прокручивается должным образом). SmoothScroll должен выполнять много работы, а не просто переходить в позицию, игнорируя скорость/время/и т. д. (требуется для "анимации").

Поэтому вы должны сделать что-то вроде:

    getListView().post(new Runnable() {
        @Override
        public void run() {
            getListView().smoothScrollToPosition(pos);
        }
    });
person Martin Marconcini    schedule 08.08.2013
comment
+1 за идею post. Однако я наткнулся на случаи, когда smoothScrollToPosition по-прежнему не работает, оставляя небольшое смещение между верхней частью виджета и элементом в позиции для прокрутки. В этих случаях setSelection (в сочетании с post, конечно) работает безупречно. - person Giulio Piancastelli; 09.10.2013
comment
Да, я тоже. ListViews намного уступают UITableView и тому подобное. Это грустно, потому что списки — это основная концепция мобильных устройств, где размер экрана скомпрометирован, поэтому нам приходится прокручивать содержимое. В любом случае, просто чтобы вы знали, прокрутка работает, нужно только понимать правила, макет должен быть заложен, вы должны сделать это в UI-треде, вы должны опубликовать его, чтобы список мог поставить операцию в очередь и т. д. Чтобы свиток появился, должно произойти много магии. Я уверен, что @RomanianGuy сможет пролить на это больше света. Тем не менее, реализация ISO по большей части работает безупречно. - person Martin Marconcini; 09.10.2013
comment
Парня зовут @RomainGuy, кстати. - person Giulio Piancastelli; 10.10.2013
comment
А, @GiulioPiancastelli, спасибо за это :) Опечатка, но сейчас я не могу ее отредактировать. - person Martin Marconcini; 10.10.2013
comment
Вот более полный обходной путь: работает как надо"> stackoverflow.com/questions/14479078/ - person Lars Blumberg; 13.08.2014
comment
Это правильный ответ :) Имеет смысл сделать так, чтобы представление само размещало его в очереди. - person The Nomad; 06.06.2017

(Скопировано из моего ответа: smoothScrollToPositionFromTop() не всегда работает как надо)

Это известная ошибка. См. https://code.google.com/p/android/issues/detail?id=36062

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

Сначала вызовите smothScrollToPositionFromTop(position), а затем, когда прокрутка закончится, вызовите setSelection(position). Последний вызов исправляет неполную прокрутку, переходя прямо в нужную позицию. При этом у пользователя по-прежнему создается впечатление, что он прокручивается с помощью анимации до этой позиции.

Я реализовал этот обходной путь с помощью двух вспомогательных методов:

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);
        }
    });
}

получить дочернюю позицию()

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;
    }
}
person Lars Blumberg    schedule 13.08.2014

Вы должны использовать метод setSelection().

person TaoZang    schedule 27.09.2012
comment
Это не обязательно одно и то же, возможно, вы не хотите, чтобы элемент был выбран, или вы просто ищете эффект сглаживания. - person mdelolmo; 06.11.2012
comment
Это на самом деле помогло мне, вместо того, чтобы ничего не происходило, оно все равно прокручивается до самого конца списка. - person Slickelito; 05.12.2012
comment
setSelection действительно работает там, где smoothScrollToPosition нет, но было бы полезно, если бы вы могли объяснить нам, почему это так. - person Giulio Piancastelli; 09.10.2013
comment
@GiulioPiancastelli smoothScrollToPosition не работает, потому что он должен немного прокручиваться в направлении желаемой позиции. Это означает, что он не будет работать для длинных списков или даже для элементов, размер которых превышает 100 dp или около того. В документации об этом ничего не сказано, но на самом деле это так. Возможно, вы сможете узнать больше, проверив фактический код реализации, если вам это интересно. - person Valerio Santinelli; 16.10.2013

Используйте LayoutManager для плавной прокрутки

layoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);
person Siddhesh Shirodkar    schedule 09.01.2019

Метод выбора набора, упомянутый Ларсом, работает, но анимация была слишком прыгучей для наших целей, поскольку она пропускала все, что осталось. Другим решением является повторный вызов метода до тех пор, пока первая видимая позиция не станет вашим индексом. Лучше всего это делать быстро и с ограничениями, так как в противном случае пользователь будет прокручивать представление.

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);
    }
}

Решение находится в С# через. Xamarin, но должен легко переводиться на Java.

person Daniel Roberts    schedule 17.09.2015

Вы звоните arrayadapter.notifyDataSetChanged() после того, как позвонили arrayadapter.add()? Также, чтобы быть уверенным, smoothScrollToPosition и setSelection являются методами, доступными в ListView, а не arrayadapter, как вы упомянули выше.

В любом случае посмотрите, поможет ли это: smoothScrollToPosition после того, как notifyDataSetChanged не работает в Android

person Code Poet    schedule 11.07.2012
comment
да .. Я сделал ошибку здесь, на форуме, это, конечно, listview.smoothScrollToPosition(pos) и listview.setSelection(pos). и notifyDataSetChanged вызывается автоматически при вызове arrayadapter.add().., иначе setSelection не будет работать - person user1515520; 11.07.2012
comment
просто странно, что setSelection(pos) работает отлично, а smoothScrollToPosition(pos) нет, не так ли? - person user1515520; 11.07.2012

используйте это в 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);
        }
    }
person Saeed Khalili    schedule 12.04.2021