RecyclerView StaggeredGridLayoutManager проблема переупорядочивания
Я пытаюсь отобразить три (по крайней мере, в этом случае у меня есть проблема) элемента в RecyclerView
с StaggeredGridLayoutManager
с двумя столбцами. Первый элемент распределен по двум рядам. Вот как это выглядит:
Теперь я перемещаю пункт "пункт 2" наверх. Вот код, который я вызываю в адаптере (это пример, который я написал, чтобы продемонстрировать проблему, которую я имею в более сложном проекте):
private int findById(int id) {
for (int i = 0; i < items.size(); ++i) {
if (items.get(i).title.equals("Item " + id)) {
return i;
}
}
return -1;
}
// Moving the item "Item 2" with id = 2 and position = 0
public void moveItem(int id, int position) {
final int idx = findById(id);
final Item item = items.get(idx);
if (position != idx) {
items.remove(idx);
items.add(position, item);
notifyItemMoved(idx, position);
//notifyDataSetChanged();
}
}
После этого массив будет в порядке: [Item 2, Item 1, Item 3]
. Однако, взгляд далек от того, чтобы отлично:
Если я коснусь RecyclerView
(достаточно, чтобы вызвать эффект overscroll, если не хватает элементов для прокрутки), пункт 2 переместится влево, где я ожидал увидеть его в первую очередь (с хорошей анимацией):
Как вы, возможно, видели в коде, я попытался заменить notifyItemMoved(idx, position)
вызовом notifyDataSetChanged()
. Это работает, но изменение не является одушевленным.
StaggeredGridLayoutManager
? Я хотел бы избежать notifyDataSetChanged()
, поскольку я хотел бы сохранить последовательность в отношении анимации.
Edit: после некоторого копания нет необходимости в полномасштабном элементе, чтобы показать проблему. Я убрал полный пролет. Когда я пытаюсь переместить пункт 2 в позицию 0, он не перемещается: пункт 1 идет за ним, а пункт 3 перемещается справа, Итак, у меня есть: пустая ячейка, пункт 2, новая строка, пункт 1, пункт 3. У меня все еще есть правильный макет после прокрутки.
Что еще интереснее, у меня нет проблемы с GridLayoutManager
. Мне нужен полнопролетный элемент, так что это не решение, но я думаю, что это действительно ошибка в StaggeredGridLayoutManager
...
2 ответа:
У меня нет полного ответа, но я могу указать вам как обходной путь, так и отчет об ошибке (который, как я полагаю, связан с этим).
Хитрость в обновлении макета, чтобы он выглядел как ваш второй скриншот, заключается в вызове
invalidateSpanAssignments()
наStaggeredGridLayoutManger
(sglm) после вызоваnotifyItemMoved()
. "Проблема" заключается в том, что если вы вызовете его сразу послеnIM()
, он не будет работать. Если вы задержите звонок на несколько МС, он будет. Так что, по-Вашему, на которые ссылается код в MainActivity, я сделал свойsglm
частный поле:private StaggeredGridLayoutManager sglm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new Adapter(); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(sglm); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter); }
И вниз в блоке коммутатора, ссылайтесь на него в обработчике:
case R.id.move_sec_top: adapter.moveItem(2, 0); new Handler().postDelayed(new Runnable() { @Override public void run() { sglm.invalidateSpanAssignments(); } }, 100); return true;
В результате ваша анимация все еще работает, макет заканчивается так, как вы хотите. Это настоящий Клудж, но он работает. Я считаю, что это та же ошибка, которую я нашел и сообщил по следующей ссылке:
Https://code.google.com/p/android/issues/detail?id=93156
В то время как мой "симптом" и требуемый вызов были разными, основная проблема, по-видимому, заключается в том, что идентичный.
Удачи!
Редактировать: не нужно postDelayed , просто публикация сделает трюк:
Моя первоначальная теория заключалась в том, что вызов был заблокирован до тех пор, пока не закончился проход макета, но я считаю, что это не так. Вместо этого я теперь думаю, что если вы вызоветеcase R.id.move_sec_top: adapter.moveItem(2, 0); new Handler().post(new Runnable() { @Override public void run() { sglm.invalidateSpanAssignments(); } }); return true;
invalidateSpanAssignments()
немедленно, он на самом деле выполняется слишком рано (до того, как изменения в макете завершатся). Таким образом, сообщение выше (без задержки) просто добавляет вызов в конец очереди рендеринга, где это происходит после верстки.
Ну, я так и сделал.
StaggeredGridLayoutManager gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); gaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); recyclerView.setLayoutManager(gaggeredGridLayoutManager); dataList = YourDataList (Your Code for Arraylist); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerAdapter = new DataAdapter(dataList, recyclerView); recyclerView.setAdapter(recyclerAdapter); // Magic line recyclerView.addOnScrollListener(new ScrollListener());
Создать класс для пользовательскогоRecyclerView Scroll Listener .
private class ScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { gaggeredGridLayoutManager.invalidateSpanAssignments(); } }
Надеюсь, это поможет вам.