smoothScrollToPositionFromTop () не всегда работает так, как должно

Я пытался какое-то время работать smoothScrollToPositionFromTop (), но не всегда переводится в нужную позицию.

У меня есть ListView (с 10 элементами) в макете с 10 кнопками сбоку, поэтому я могу прокручивать каждый элемент в списке. Обычно, когда я прокручиваю одну позицию назад или вперед, она работает нормально, но часто, когда я пытаюсь прокручивать более трех позиций назад или вперед, ListView точно не заканчивается в выбранной позиции. Когда он терпит неудачу, он обычно заканчивается от 0,5 до 1,5 пунктов, и на самом деле он не предсказуем, когда прокрутка выходит из строя.

Я также проверил smoothScrollToPosition после того, как notifyDataSetChanged не работает в android , но это исправление не работает для меня, и я не изменяю никаких данных.

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

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

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

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

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

smoothScrollToPositionFromTop ()

 public static void smoothScrollToPositionFromTop(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; } } 

Вот реализация решения.

  void smoothScrollToPositionFromTopWithBugWorkAround(final AbsListView listView, final int position, final int offset, final int duration){ //the bug workaround involves listening to when it has finished scrolling, and then //firing a new scroll to the same position. //the bug is the case that sometimes smooth Scroll To Position sort of misses its intended position. //more info here : https://code.google.com/p/android/issues/detail?id=36062 listView.smoothScrollToPositionFromTop(position, offset, duration); listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(scrollState==OnScrollListener.SCROLL_STATE_IDLE){ listView.setOnScrollListener(null); listView.smoothScrollToPositionFromTop(position, offset, duration); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } 

Как упоминается на странице google issuetracker на четвертом этаже: https://issuetracker.google.com/issues/36952786

Обходной путь, описанный ранее: «Обходной путь на данный момент прослушивает SCROLL_STATE_IDLE при запуске свитка и smoothScrollToPositionFromTop снова в ту же позицию». не всегда будут работать.

Фактически, вызов onScrollStateChanged с помощью SCROLL_STATE_IDLE не обязательно означает, что прокрутка завершена. В результате он все еще не может гарантировать, что Listview каждый раз прокручивается до правильной позиции, особенно когда представления элементов списка не одинаковы.

После исследования я нашел другой подход, который работает совершенно правильно и разумно. Как известно, Listview предоставляет метод scrollListBy (int y), который позволяет нам мгновенно прокручивать список List с y пикселями. Затем, с помощью таймера, мы можем прокручивать список плавно и правильно сами.

Первое, что нам нужно сделать, это вычислить высоту каждого представления элемента списка, включая представления за пределами экрана. Поскольку данные списка и типы дочерних представлений уже известны ранее, можно вычислить высоту каждого представления элемента списка. Таким образом, при заданной позиции для прокрутки до плавного, мы можем рассчитать его расстояние прокрутки в направлении y. Кроме того, расчет должен быть выполнен после завершения инициализации ListView.

Во-вторых, это сочетание таймера и метода scrollListBy (int). На самом деле мы можем использовать метод sendEmptyMessageDelayed () android.os.Handler. Таким образом, решение может быть:

 /** * Created by CaiHaozhong on 2017/9/29. */ public class ListViewSmoothScroller { private final static int MSG_ACTION_SCROLL = 1; private final static int MSG_ACTION_ADJUST = 2; private ListView mListView = null; /* The accumulated height of each list item view */ protected int[] mItemAccumulateHeight = null; protected int mTimeStep = 20; protected int mHeaderViewHeight; private int mPos; private Method mTrackMotionScrollMethod = null; protected int mScrollUnit = 0; protected int mTotalMove = 0; protected int mTargetScrollDis = 0; private Handler mMainHandler = new Handler(Looper.getMainLooper()){ public void handleMessage(Message msg) { int what = msg.what; switch (what){ case MSG_ACTION_SCROLL: { int scrollDis = mScrollUnit; if(mTotalMove + mScrollUnit > mTargetScrollDis){ scrollDis = mTargetScrollDis - mTotalMove; } if(Build.VERSION.SDK_INT >= 19) { mListView.scrollListBy(scrollDis); } else{ if(mTrackMotionScrollMethod != null){ try { mTrackMotionScrollMethod.invoke(mListView, -scrollDis, -scrollDis); }catch(Exception ex){ ex.printStackTrace(); } } } mTotalMove += scrollDis; if(mTotalMove < mTargetScrollDis){ mMainHandler.sendEmptyMessageDelayed(MSG_ACTION_SCROLL, mTimeStep); }else { mMainHandler.sendEmptyMessageDelayed(MSG_ACTION_ADJUST, mTimeStep); } break; } case MSG_ACTION_ADJUST: { mListView.setSelection(mPos); break; } } } }; public ListViewSmoothScroller(Context context, ListView listView){ mListView = listView; mScrollUnit = Tools.dip2px(context, 60); mPos = -1; try { mTrackMotionScrollMethod = AbsListView.class.getDeclaredMethod("trackMotionScroll", int.class, int.class); }catch (NoSuchMethodException ex){ ex.printStackTrace(); mTrackMotionScrollMethod = null; } if(mTrackMotionScrollMethod != null){ mTrackMotionScrollMethod.setAccessible(true); } } /* scroll to a target position smoothly */ public void smoothScrollToPosition(int pos){ if(mListView == null) return; if(mItemAccumulateHeight == null || pos >= mItemAccumulateHeight.length){ return ; } mPos = pos; mTargetScrollDis = mItemAccumulateHeight[pos]; mMainHandler.sendEmptyMessage(MSG_ACTION_SCROLL); } /* call after initializing ListView */ public void doMeasureOnLayoutChange(){ if(mListView == null){ return; } int headerCount = mListView.getHeaderViewsCount(); /* if no list item */ if(mListView.getChildCount() < headerCount + 1){ return ; } mHeaderViewHeight = 0; for(int i = 0; i < headerCount; i++){ mHeaderViewHeight += mListView.getChildAt(i).getHeight(); } View firstListItemView = mListView.getChildAt(headerCount); computeAccumulateHeight(firstListItemView); } /* calculate the accumulated height of each list item */ protected void computeAccumulateHeight(View firstListItemView){ int len = listdata.size();// count of list item mItemAccumulateHeight = new int[len + 2]; mItemAccumulateHeight[0] = 0; mItemAccumulateHeight[1] = mHeaderViewHeight; int currentHeight = mHeaderViewHeight; for(int i = 2; i < len + 2; i++){ currentHeight += getItemHeight(firstListItemView); mItemAccumulateHeight[i] = currentHeight; } } /* get height of a list item. You may need to pass the listdata of the list item as parameter*/ protected int getItemHeight(View firstListItemView){ // Considering the structure of listitem View and the list data in order to calculate the height. } } 

После завершения инициализации нашего ListView мы вызываем метод doMeasureOnLayoutChange (). После этого мы можем прокрутить ListView с помощью метода smoothScrollToPosition (int pos). Мы можем вызвать метод doMeasureOnLayoutChange () следующим образом:

 mListAdapter.notifyDataSetChanged(); mListView.post(new Runnable() { @Override public void run() { mListViewSmoothScroller.doMeasureOnLayoutChange(); } }); 

Наконец, наш ListView можно прокручивать в целевую позицию плавно и, что более важно, правильно.

  • Понимание fragmentа setRetainInstance (boolean)
  • Класс Android LocationClient устарел, но используется в документации
  • Неверный fragment в ViewPager получает onContextItemВыбранный вызов
  • java.lang.noclassdeffounderror: org.ksoap2.serialization.SoapObject
  • как отображать карту в android с маркером
  • Вам нужно сохранить высокий балл для Android-игры
  • Фрагмент над другим fragmentом
  • Простой способ сделать динамическую, но квадратную компоновку
  • Два основных действия в AndroidManifest.xml
  • Когда следует использовать Theme.AppCompat против ThemeOverlay.AppCompat?
  • Как прослушивать дуплекс на представлении в Android?
  • Interesting Posts

    RAID 1 (зеркало) – как привод соответствует определенному порту SATA

    Неисправность BIOS HP Pavilion. Не удалось восстановить

    Десериализация дат с форматом dd / mm / yyyy с использованием Json.Net

    дата Сортировка проблемы с JQuery Tablesorter

    Где разместить общую библиотеку в JBoss AS 5?

    Угловой порядок выполнения с `$ q` – Цепочные обещания

    Как предотвратить автоматический вход последнего пользователя в Windows 10?

    Как установить параметр eclipse.ini -vm?

    Как определить, были ли внесены изменения в настройках?

    Экранирование% в именах файлов / папок в командной строке

    Запустить командный файл с командой psql без пароля

    Отслеживание использования приложений в течение дня

    Итальянская клавиатура: ввод символов тильды (~) и обратного (`) без изменения раскладки клавиатуры

    Простой пример XML-анализа libxml2 с использованием Objective-c, Xcode и HTMLparser.h

    Часы Windows, показывающие как UTC, так и местное время?

    Давайте будем гением компьютера.