RecyclerView und SwipeRefreshLayout

? Lukas Olsen @ | Original: StackOverFlow
---

Ich bin mit der neuen RecyclerView - Layouts in einem SwipeRefreshLayout und erlebt ein seltsames Verhalten . Beim Scrollen Sie die Liste wieder an die Spitze manchmal der Blick auf die Oberseite wird in geschnitten.

RecyclerView und SwipeRefreshLayout

Wenn ich versuche, an die Spitze jetzt blättern - die Pull -To- Refresh auslöst.

RecyclerView und SwipeRefreshLayout

Wenn ich versuche, und entfernen Sie die Swipe - Refresh - Layouts auf der Recycler -View das Problem ist weg. Und seine reproduzierbare auf beliebigen Telefon ( nicht nur L- Vorschau Geräte).

 <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>

Das ist mein Layout - die Zeilen dynamisch vom RecyclerViewAdapter (2 Viewtypes in dieser Liste ) gebaut.

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;
}

Hier ist der Adapter .

   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));

Und der Code des Fragments, das die Recycler und die SwipeRefreshLayout .

Wenn jemand anderes hat dieses Problem erfahren und es gelöst oder zumindest den Grund dafür zu finden ?

---

Top 5 Antwort

1krunal patel @

Schreiben Sie den folgenden Code in setOnScrollListener der 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 @

leider ist dies ein bekannter Fehler in LinearLayoutManager . Es ist nicht richtig, wenn der erste computeScrollOffset Punkt sichtbar ist . wird festgelegt, wenn sie losgelassen wird .

3Tom91136 @

Bevor Sie diese Lösung verwenden: RecyclerView ist noch nicht abgeschlossen ist, versuchen Sie nicht, es in der Produktion verwenden, wenn Sie wie ich !

Wie für November 2014, gibt es immer noch Bugs in RecyclerView, die dazu führen würde canScrollVertically vorzeitig return false . Diese Lösung wird alle Scrollen Probleme zu lösen.

Der Abfall der Lösung :

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);

    }
}

Sie brauchen noch nicht einmal zu ersetzen RecyclerView in Ihrem Code mit FixedRecyclerView, anstelle der XML-Tag ausreichen würde ! ( Das stellt sicher, dass, wenn RecyclerView abgeschlossen ist, würde der Übergang sein, schnell und einfach )

Explanation:

Grundsätzlich canScrollVertically(boolean) false zurück zu früh ist, so dass wir prüfen, ob die RecyclerView wird den ganzen Weg an die Spitze der ersten Ansicht ( wo die ersten Kindes oben würde 0 sein) und dann wieder durchgeblättert.

EDIT: Und wenn Sie nicht möchten, dass aus irgendeinem Grund zu verlängern RecyclerView, die Sie erweitern können SwipeRefreshLayout und überschreiben Sie die Methode canChildScrollUp() und setzen Sie die Prüflogik drin.

EDIT 2 : RecyclerView wurde veröffentlicht und bisher gibt es keine Notwendigkeit, um dieses Update zu verwenden.

4balachandarkm @

Ich stieß auf das gleiche Problem vor kurzem. Ich habe versucht, den Ansatz Krunal_Patel vorgeschlagen, aber es funktionierte die meiste Zeit in meinem Nexus 4 und überhaupt nicht in der Samsung-Galaxie s2 zu arbeiten. Während des Debuggens ist recyclerView.getChildAt (0) .getTop () immer nicht richtig für RecyclerView . Also, Nachdem man durch verschiedene Methoden, dachte ich, dass wir die Verwendung des Verfahrens findFirstCompletelyVisibleItemPosition ( ) des Layout machen, um vorherzusagen, ob das erste Element des RecyclerView sichtbar ist oder nicht, um zu ermöglichen SwipeRefreshLayout.Find den Code unten . Hoffe, dass es jemand versuchen, das gleiche Problem zu beheben hilft . Zum Wohle.

    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 @

Ich habe dieselbe Problem aufgetreten ist . Ich löste es, indem Sie blättern Listener, bis voraussichtlich erste sichtbare Element warten wird auf der RecyclerView gezogen . Sie können andere blättern Hörer zu binden, entlang dieser. Erwartete ersten sichtbaren Wert hinzugefügt, um es als Schwellenposition verwenden, wenn die SwipeRefreshLayout sollte in Fällen, in denen Sie Kopfsicht Inhaber aktiviert sein.

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);