1

Я пытаюсь сделать макет, похожий на то, как работает макет фильтра Instagram. В основном, когда вы выбираете фильтр, он будет прокручиваться до выбранного вами элемента + 1, показывая, что есть больше фильтров.LinearLayoutManager плавный прокрутки перескакивает в неправильное положение

В настоящее время я пытаюсь построить пользовательский LinearLayoutManager для моей горизонтальной RecyclerView здесь:

public class LinearLayoutSnapManager extends LinearLayoutManager { 
    private int mCurrentPos = 0; 

    public LinearLayoutSnapManager(Context context) { 
     super(context); 
    } 

    public LinearLayoutSnapManager(Context context, int orientation, boolean reverseLayout) { 
     super(context, orientation, reverseLayout); 
    } 

    public LinearLayoutSnapManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
     super(context, attrs, defStyleAttr, defStyleRes); 

    } 


    public void snap(RecyclerView rv, int position) { 
     if (mCurrentPos == position) { 
      // No move 
      return; 
     } 

     boolean goingRight = true; 
     if (position < mCurrentPos) { 
      goingRight = false; 
     } 
     mCurrentPos = position; 
     smoothScrollToPosition(rv, new RecyclerView.State(), goingRight ? getScrollRightPos(): getScrollLeftPos()); 
    } 

    private int getScrollLeftPos() { 
     int newPos = mCurrentPos - 1; 
     return (newPos > 0) ? newPos : 0; 
    } 

    private int getScrollRightPos() { 
     return mCurrentPos + 1; 
    } 
} 

скроллинг слева работает так же, как предназначенные, но когда я прокрутки вправо кажется просто перейти в конец списка против newItem + 1, и я не могу понять, почему это происходит.

enter image description here

+0

Не могли бы вы разместить больше кода, я имею в виду, как вы используете эти методы? Ваша логика кажется прекрасной, но вы можете передать позицию в неподходящий момент, то есть – FabioR

+0

Это может быть так, что когда вызывается 'getScrollRightPos', старая (и не обновляемая) позиция передается в mCurrentPos, поэтому она прокручивается до OLD position + 1 (что может быть в конце recyclerview). Также мне кажется, что у вас есть проблема, когда вы нажимаете элементы, которые не смежны с текущим элементом, является ли свиток, работающий в этих случаях? – FabioR

+0

Кто вызывает 'snap()'? Откуда появляется int позиция? – g4s8

ответ

1

Я звонил в мой метод snap в обратном вызове интерфейса из onclicklistener в своем горизонтальном списке. Перед вызовом интерфейса я устанавливал выделенный элемент, а затем уведомлял, что представление изменилось, чтобы обновить текущее состояние. Это вызвало все проблемы с возвратом неверных индексов менеджером layoutmanager.

Я обнаружил это однажды, когда создал пустой проект и протестировал свою логику. К моему удивлению, сработал штраф, который позволил мне отследить реальную проблему.

Как только я переместил snaplogic перед всеми моими логическими схемами, все работало по назначению без изменения кода, который я опубликовал.

Надеюсь, это может помочь кому-то в будущем.

0

Если вы хотите, последний выбранный фильтр отображается во-вторых (один фильтр перед тем, несколько фильтров, после), вы не имеете правильный подход.

В этом случае, независимо от направления справа или слева, процесс будет таким же. Позиция RecyclerView всегда на 1 меньше, чем позиция выбранного фильтра, за исключением первого элемента.

(Этот пример не оптимизирован для последней позиции)

public class LinearLayoutSnapManager extends LinearLayoutManager { 
    private int mCurrentSelectedPos = 0; 

    public LinearLayoutSnapManager(Context context) { 
     super(context); 
    } 

    public LinearLayoutSnapManager(Context context, int orientation, boolean reverseLayout) { 
     super(context, orientation, reverseLayout); 
    } 

    public LinearLayoutSnapManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
     super(context, attrs, defStyleAttr, defStyleRes); 

    } 


    public void snap(RecyclerView rv, int selectedPosition) { 
     if (mCurrentSelectedPos == selectedPosition) { 
      // No move 
      return; 
     } 

     mCurrentSelectedPos = selectedPosition; 

     int newDisplayPos; 
     if (mCurrentSelectedPos > 0) { 
      newDisplayPos = mCurrentSelectedPos - 1; 
     } else { 
      newDisplayPos = 0; 
     } 

     smoothScrollToPosition(rv, new RecyclerView.State(), newDisplayPos); 
    } 

} 
+0

Этот код имеет ту же проблему, что и выше, вместо того, чтобы прыгать при прокрутке вправо, ничего не происходит. При прокрутке вправо мне нужно, чтобы он вел себя так, как это происходит при прокрутке влево. – Nick

1

Я хотел бы предложить другое решение. LinearLayoutManager поставляется с удобными функциями:

int findFirstCompletelyVisibleItemPosition()

Возвращает позицию адаптера первого полностью видимого зрения.


int findLastCompletelyVisibleItemPosition()

Возвращает позицию адаптера последнего полностью видимого зрения.


int findFirstVisibleItemPosition()

Возвращает позицию адаптера первого видимого зрения.


int findFirstVisibleItemPosition()

Возвращает позицию адаптера последней видимой точки зрения.

Первые две функции возвращают позиции полностью видимых видов, тогда как последние два возвращают даже частично видимые виды. Выбор зависит от вас, какое поведение вы бы хотели достичь.

При их использовании функции getScrollLeftPos и getScrollRightPos должен выглядеть следующим образом:

private int getScrollLeftPos() { 
    int newPos = findFirstCompletelyVisibleItemPosition() - 1; 
    return (newPos > 0) ? newPos : 0; 
} 

private int getScrollRightPos() { 
    return findLastCompletelyVisibleItemPosition()+ 1; 
} 

не вызывать метод:

smoothScrollToPosition(rv, new RecyclerView.State(), goingRight ? getScrollRightPos(): getScrollLeftPos()); 

из LayoutManager. Если у вас есть ссылка на RecyclerView, вызовите:

rv.smoothScrollToPosition(newPosition); 

Если вы посмотрите исходный код RecyclerView вы найдете такую ​​реализацию:

public void smoothScrollToPosition(int position) { 
    if (mLayoutFrozen) { 
     return; 
    } 
    if (mLayout == null) { 
     Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " + 
       "Call setLayoutManager with a non-null argument."); 
     return; 
    } 
    mLayout.smoothScrollToPosition(this, mState, position); 
} 

Это в основном так же, как вы это делаете, но быть что функция LayoutManager вызывается с соответствующими RecyclerView.State. Это просто соответствует RecyclerView

+1

Итак, я думаю, что суть проблемы - 'findLastVisibleItemPosition()', похоже, не прав. Когда я нажимаю на второй элемент в списке, он возвращает 0. То же самое с 'findLastCompletelyVisibleItemPosition()'. 'Getchildcount()' равно 1, когда он выглядит, есть 4 представления, прикрепленные к просмотру ресайклеров. – Nick

Смежные вопросы