RecyclerView и SwipeRefreshLayout

? Lukas Olsen @ | Original: StackOverFlow
---

Я использую новый RecyclerView - макета в SwipeRefreshLayout и испытал странное поведение. При прокрутке списка наверх иногдавид сверху урезается в .

RecyclerView и SwipeRefreshLayout

Если я пытаюсь, чтобы перейти к верхней Сейчас - Pull- для-обновления триггеров.

RecyclerView и SwipeRefreshLayout

Если я пытаюсь и удалить Проведите Обновить верстки вокруг Recycler -Viewпроблема ушла . И его воспроизводимость на любой телефон ( не только L- Предпросмотр устройств) .

 <android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/contentView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/hot_fragment_recycler"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.v4.widget.SwipeRefreshLayout>

Это мой макет - строки строятся динамически RecyclerViewAdapter ( 2 Viewtypes в этом списке ).

public class HotRecyclerAdapter extends TikDaggerRecyclerAdapter<GameRow> {

private static final int VIEWTYPE_GAME_TITLE = 0;
private static final int VIEWTYPE_GAME_TEAM = 1;

@Inject
Picasso picasso;

public HotRecyclerAdapter(Injector injector) {
    super(injector);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position, int viewType) {
    switch (viewType) {
        case VIEWTYPE_GAME_TITLE: {
            TitleGameRowViewHolder holder = (TitleGameRowViewHolder) viewHolder;
            holder.bindGameRow(picasso, getItem(position));
            break;
        }
        case VIEWTYPE_GAME_TEAM: {
            TeamGameRowViewHolder holder = (TeamGameRowViewHolder) viewHolder;
            holder.bindGameRow(picasso, getItem(position));
            break;
        }
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    switch (viewType) {
        case VIEWTYPE_GAME_TITLE: {
            View view = inflater.inflate(R.layout.game_row_title, viewGroup, false);
            return new TitleGameRowViewHolder(view);
        }
        case VIEWTYPE_GAME_TEAM: {
            View view = inflater.inflate(R.layout.game_row_team, viewGroup, false);
            return new TeamGameRowViewHolder(view);
        }
    }
    return null;
}

@Override
public int getItemViewType(int position) {
    GameRow row = getItem(position);
    if (row.isTeamGameRow()) {
        return VIEWTYPE_GAME_TEAM;
    }
    return VIEWTYPE_GAME_TITLE;
}

Вотадаптер .

   hotAdapter = new HotRecyclerAdapter(this);

    recyclerView.setHasFixedSize(false);
    recyclerView.setAdapter(hotAdapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    contentView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            loadData();
        }
    });

    TypedArray colorSheme = getResources().obtainTypedArray(R.array.main_refresh_sheme);
    contentView.setColorSchemeResources(colorSheme.getResourceId(0, -1), colorSheme.getResourceId(1, -1), colorSheme.getResourceId(2, -1), colorSheme.getResourceId(3, -1));

Икод фрагмент, содержащий Recycler и SwipeRefreshLayout .

Если кто-то испытал это поведение и решить ее или, по крайней мере, нашли причину этого ?

---

Top 5 ответ

1krunal patel @

написать следующий код в setOnScrollListener в ​​RecyclerView

example:

        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(int i) {

        }

        @Override
        public void onScrolled(int i, int i2) {
            int topRowVerticalPosition =
                    (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
            swipeRefresh.setEnabled(topRowVerticalPosition >= 0);
        }
    });
2yigit @

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

3Tom91136 @

Перед тем, как использовать это решение : RecyclerView еще не завершена, старайтесь не использовать его в производстве, если вы не, как я!

Как в ноябре 2014 года, есть еще ошибки в RecyclerView, которые могут привести canScrollVertically, чтобы вернуться ложным преждевременно. Это решение позволит решить все прокрутки проблемы.

Падение в растворе :

public class FixedRecyclerView extends RecyclerView {
    public FixedRecyclerView(Context context) {
        super(context);
    }

    public FixedRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FixedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean canScrollVertically(int direction) {
        // check if scrolling up
        if (direction < 1) {
            boolean original = super.canScrollVertically(direction);
            return !original && getChildAt(0) != null && getChildAt(0).getTop() < 0 || original;
        }
        return super.canScrollVertically(direction);

    }
}

Вы даже не нужно, чтобы заменить RecyclerView в коде с FixedRecyclerView, заменив XML тег будет достаточно ! (Гарантирует, что при RecyclerView завершена,переход будет простым и быстрым )

Explanation:

В принципе, canScrollVertically(boolean) возвращает ложь рано, поэтому мы проверяем, еслиRecyclerView прокручивается весь путь к вершине первого взгляда (где верхний первого ребенка будет 0 ), а затем вернуться.

EDIT: И если вы не хотите, чтобы расширить RecyclerView по какой-то причине, вы можете продлить SwipeRefreshLayout и переопределить метод canChildScrollUp() и поставить логику проверки в там.

EDIT2 : RecyclerView был выпущен и до сих пор нет никакой необходимости использовать данное исправление.

4balachandarkm @

Недавно я наткнулся на одной и той же проблемы. Я попытался подход, предложенный Krunal_Patel, но он работал большую часть времени в моей Nexus 4 и не работать вообще в Samsung Galaxy S2 . Во время отладки, recyclerView.getChildAt ( 0 ) .getTop ( ) всегда не подходит для RecyclerView . Таким образом, после прохождения через различные методы, я подумал, что мы можем сделать использование метода findFirstCompletelyVisibleItemPosition ( ) в LayoutManager предсказать, будет ли видна или непервый пункт RecyclerView, с тем чтобы SwipeRefreshLayout.Findкод ниже . Надеюсь, что это помогает кто-то пытается исправить тот же вопрос . Приветствия .

    recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        }

        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            swipeRefresh.setEnabled(linearLayoutManager.findFirstCompletelyVisibleItemPosition() == 0);
        }
    });
5Nikola Despotoski @

Я испытал тот же вопрос . Я решил, добавив прокрутки слушателю, что не будет ждать, пока ожидается первый видимый элемент будет обращено наRecyclerView . Вы можете связать другие прокрутки слушателей тоже по этому одному . Ожидаемый первый видимый значение добавляется использовать его в качестве порогового положении, когдаSwipeRefreshLayout должна быть включена в тех случаях, когда вы используете заголовков держатели просмотра.

public class SwipeRefreshLayoutToggleScrollListener extends RecyclerView.OnScrollListener {
        private List<RecyclerView.OnScrollListener> mScrollListeners = new ArrayList<RecyclerView.OnScrollListener>();
        private int mExpectedVisiblePosition = 0;

        public SwipeRefreshLayoutToggleScrollListener(SwipeRefreshLayout mSwipeLayout) {
            this.mSwipeLayout = mSwipeLayout;
        }

        private SwipeRefreshLayout mSwipeLayout;
        public void addScrollListener(RecyclerView.OnScrollListener listener){
            mScrollListeners.add(listener);
        }
        public boolean removeScrollListener(RecyclerView.OnScrollListener listener){
            return mScrollListeners.remove(listener);
        }
        public void setExpectedFirstVisiblePosition(int position){
            mExpectedVisiblePosition = position;
        }
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            notifyScrollStateChanged(recyclerView,newState);
            LinearLayoutManager llm = (LinearLayoutManager) recyclerView.getLayoutManager();
            int firstVisible = llm.findFirstCompletelyVisibleItemPosition();
            if(firstVisible != RecyclerView.NO_POSITION)
                mSwipeLayout.setEnabled(firstVisible == mExpectedVisiblePosition);

        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            notifyOnScrolled(recyclerView, dx, dy);
        }
        private void notifyOnScrolled(RecyclerView recyclerView, int dx, int dy){
            for(RecyclerView.OnScrollListener listener : mScrollListeners){
                listener.onScrolled(recyclerView, dx, dy);
            }
        }
        private void notifyScrollStateChanged(RecyclerView recyclerView, int newState){
            for(RecyclerView.OnScrollListener listener : mScrollListeners){
                listener.onScrollStateChanged(recyclerView, newState);
            }
        }
    }

Usage:

SwipeRefreshLayoutToggleScrollListener listener = new SwipeRefreshLayoutToggleScrollListener(mSwiperRefreshLayout);
listener.addScrollListener(this); //optional
listener.addScrollListener(mScrollListener1); //optional
mRecyclerView.setOnScrollLIstener(listener);