2015-02-11 4 views
3

Моя задача - реализовать элемент горизонтального элемента в RecyclerView. Я на самом деле справился с задачей благодаря исходному коду ViewPager и некоторым другим ресурсам, но имею проблемы с одним сценарием.RecyclerView, item swiping, java.lang.IllegalStateException

My SwipeableRecyclerView (SRV с этого момента) расширяет RecyclerView и реализует RecyclerView.OnItemTouchListener для выполнения магии, которая на самом деле работает. Он также определяет настраиваемый интерфейс SwipeListener, который определяет несколько методов, наиболее важным (для этого вопроса) является «onSwipe (View view, int position, boolean right)».

Моя деятельность поставляет реализацию интерфейса к СРВУ, и когда салфетки обнаружена и метод слушателя уволен, он выполняет следующие действия:

  • перемещает элемент/прокатывается вид с экрана в правильное направление с помощью 'view.animate(). translationX (translationX)'
  • В приведенной выше анимации есть слушатель, который в onAnimationEnd удаляет элемент в указанной позиции из адаптера и вызывает адаптер.notifyItemRemoved (position)
  • результатом является то, что вид исчезает со стороны, а затем переработчик удаляет строку с ее анимацией - все это очень хорошо работает на самом деле

Проблема возникает, когда я прокручиваю очень быстро, так как в то время, когда первая анимация еще не завершена, я начинаю прокручивать другую строку. Иногда он создает «дыры»/фантомные строки без представления; Иногда, я получаю следующее исключение:

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling 

и трассировки стека показывает, что adapter.notifyItemRemoved() является метод вызывает исключение.

Для задачи исключения, последовательность событий выглядит следующим образом (из моих отладки следов):

started for position: 11 
started for position: 10 
finished for position: 11 
removing 11 
removed 11 
finished position: 10 
removing 10 

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

Итак, я в рассоле, я понятия не имею, что с этим делать. На самом деле, я думаю, что проще всего отключить прикосновения всего вида ресайклера при запуске анимации и включить их снова только после прокрутки. Я не могу отключить SRV, хотя - я попробовал setEnabled (false), clickable и т. Д., Но ничего не работает.

Любая помощь?

ответ

2

Вот что в конечном итоге работает для меня:

  • добавить swipingEnabled свойство СРВ; когда false, onInterceptTouchEvent просто вызывает родительскую реализацию, эффективно отключая прокрутку элемента.
  • Добавить этот код в метод, который отвечает за обработку жестов (например, удаление записи и т. д.).):

    swipeableRecyclerView.setSwipingEnabled(false); 
    
  • добавить этот код рядом, где создается SRV:

    swipeableRecyclerView.setItemAnimator(new DefaultItemAnimator() { 
    
        @Override 
        public void onRemoveFinished(RecyclerView.ViewHolder item) { 
         swipeableRecyclerView.setSwipingEnabled(true); 
        } 
    }); 
    

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

Теперь мой переводчик «сериализуется», и никаких исключений не происходит.

+2

Я нашел использование onMoveFinished лучше, так как это учитывает перестановки после того, как элемент удален. – Ljdawson

+0

Означает ли это, что после выполнения onRemoveFinished() выполняется целая последовательность ходов? Я предположил (может быть, ошибочно), что удаление охватывает всю операцию ... – wujek

+1

Я протестировал его, и это действительно так: после onRemoveFinished существует целая куча onMoveFinished вызовов. Спасибо за совет. – wujek

3

Вы можете проверить на mRecyclerView.getItemAnimator().isRunning(), немного легче, чем на текущий ответ. В то время как true, MotionEvent s не должно вызывать никакого удаления.

+0

Я тестировал это, и он не работает. Я все еще могу провести два или более просмотров, чтобы их анимации перекрывались, и это привело к сбою. Согласитесь, может быть что-то не так в остальной части моего кода, я тоже буду испытывать больше. Спасибо за сообщение, однако, я не знал о методе, который вы упомянули, и его сестре, которая принимает слушателя - классный материал. – wujek

+0

Возможно, это работает для меня. Анимация, похоже, запускается адаптером.notifyItemRemoved(). – natario

+0

Я понимаю, почему это не работает для меня: когда я обнаруживаю увольнение, в моем решении (суррогатный флаг) я сразу же отключил любую другую прокрутку и начал финишную анимацию, чей метод прослушивателя onAnimationEnd удаляет элемент и вызывает notifyItemRemoved() , в результате чего анимация удаления начнется. Предлагаемое вами решение вводит очень короткое окно (с момента запуска моей анимации завершения салфетки до тех пор, пока не будет вызван метод notifyItemRemoved(), начиная анимацию). (Продолжение следует в другом комментарии). – wujek