Wie zeige ich eine leere Ansicht mit einer RecyclerView?

271

Ich bin es gewohnt, eine spezielle Ansicht in die Layoutdatei einzufügen, wie in der ListActivityDokumentation beschrieben , die angezeigt wird, wenn keine Daten vorhanden sind . Diese Ansicht hat die ID "android:id/empty".

<TextView
    android:id="@android:id/empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/no_data" />

Ich frage mich, wie das mit dem neuen gemacht werden kann RecyclerView.

JJD
quelle
2
Sie können github.com/rockerhieu/rv-adapter-states verwenden . Es unterstützt nicht nur die leere Ansicht, sondern auch die Ladeansicht und die Fehleransicht. Und Sie können es verwenden, ohne die Logik des vorhandenen Adapters zu ändern.
Hieu Rocker
20
Es ist nichts anderes als faszinierend, dass es keine einfache setEmptyView()Methode gegen eine RecyclerView...
Subby
Hier ist meine Antwort, bitte überprüfen Sie diesen Link stackoverflow.com/a/58411351/5449220
Rakesh Verma
@Subby, stimme zu, hatte aber setEmptyView()auch Nachteil. Beim Laden von Daten wollten wir keine leere Ansicht anzeigen ListView, aber das tat es. Also mussten wir eine Logik implementieren. Derzeit verwende ich stackoverflow.com/a/48049142/2914140 .
CoolMind

Antworten:

305

Fügen Sie in demselben Layout, in dem das definiert ist RecyclerView, Folgendes hinzu TextView:

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

<TextView
    android:id="@+id/empty_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:visibility="gone"
    android:text="@string/no_data_available" />

Beim onCreateoder beim entsprechenden Rückruf prüfen Sie, ob der Datensatz, der Sie speist, RecyclerViewleer ist. Wenn der Datensatz leer ist, RecyclerViewist auch der Datensatz leer. In diesem Fall wird die Meldung auf dem Bildschirm angezeigt. Wenn nicht, ändern Sie die Sichtbarkeit:

private RecyclerView recyclerView;
private TextView emptyView;

// ...

recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
emptyView = (TextView) rootView.findViewById(R.id.empty_view);

// ...

if (dataset.isEmpty()) {
    recyclerView.setVisibility(View.GONE);
    emptyView.setVisibility(View.VISIBLE);
}
else {
    recyclerView.setVisibility(View.VISIBLE);
    emptyView.setVisibility(View.GONE);
}
Slellis
quelle
11
Das funktioniert bei mir nicht, da das Layout der Recycler-Ansicht im Fragment aufgeblasen ist, während die Entscheidung, ob ein normales Elementlayout oder ein Layout ohne Elemente angezeigt werden soll, im Adapter getroffen wird.
JJD
1
@JJD Bei Verwendung eines Fragments gilt das Gleiche. Wenn Sie Ihre Fragmentansicht aufblasen, haben Sie die Recycling-Ansicht und dann ein anderes Layout, das für Ihre leere Ansicht verwendet wird. Normalerweise habe ich in meinen Fragmenten die folgenden Ansichten: 1) Recycling-Ansicht, 2) leere Ansicht und 3) Fortschrittsbalkenansicht. Normalerweise übergebe ich den Adapter unabhängig von der Liste, aber basierend auf der Größe der Liste werde ich entweder die Recycling-Ansicht ausblenden und die leere anzeigen oder umgekehrt. Wenn eine Recycling-Ansicht eine leere Liste enthält, hat sie nichts damit zu tun. Sie sehen nur eine leere Ansicht.
Ray Hunter
1
@stackex Die Überprüfung muss im onCreateView-Rückruf des Fragments oder in onResume of Activity erfolgen. Auf diese Weise wird die Überprüfung unmittelbar vor der Anzeige des Bildschirms für den Benutzer durchgeführt und funktioniert für beide, wobei die Anzahl der Zeilen im Datensatz erhöht oder verringert wird.
Slellis
2
@Zapnologica Sie können es auch umgekehrt machen: Verwenden Sie einen Eventbus (z. B. Greenrobots Eventbus oder Otto) und lassen Sie den Adapter auf dem Eventbus posten, wenn sich der Datensatz ändert. In dem Fragment erstellen Sie lediglich eine Methode, deren Parameter dem entsprechen, was der Adapter an die Eventbus.post-Methode übergibt, und ändern dann das Layout entsprechend. Ein einfacherer, aber auch gekoppelter Ansatz besteht darin, eine Rückrufschnittstelle im Adapter zu erstellen und diese beim Erstellen des Adapters vom Fragment implementieren zu lassen.
AgentKnopf
2
Im Layout bekomme ich mehrere Root-Tags. Wie sieht Ihre vollständige Layoutdatei aus?
Pulver366
192

Für meine Projekte habe ich diese Lösung ( RecyclerViewmit setEmptyViewMethode) gemacht:

public class RecyclerViewEmptySupport extends RecyclerView {
    private View emptyView;

    private AdapterDataObserver emptyObserver = new AdapterDataObserver() {


        @Override
        public void onChanged() {
            Adapter<?> adapter =  getAdapter();
            if(adapter != null && emptyView != null) {
                if(adapter.getItemCount() == 0) {
                    emptyView.setVisibility(View.VISIBLE);
                    RecyclerViewEmptySupport.this.setVisibility(View.GONE);
                }
                else {
                    emptyView.setVisibility(View.GONE);
                    RecyclerViewEmptySupport.this.setVisibility(View.VISIBLE);
                }
            }

        }
    };

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

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

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

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);

        if(adapter != null) {
            adapter.registerAdapterDataObserver(emptyObserver);
        }

        emptyObserver.onChanged();
    }

    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
    }
}

Und Sie sollten es anstelle der RecyclerViewKlasse verwenden:

<com.maff.utils.RecyclerViewEmptySupport android:id="@+id/list1"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    />

<TextView android:id="@+id/list_empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Empty"
    />

und

RecyclerViewEmptySupport list = 
    (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1);
list.setLayoutManager(new LinearLayoutManager(context));
list.setEmptyView(rootView.findViewById(R.id.list_empty));
maff91
quelle
2
Haben Sie diesen Code aus dieser Antwort oder von hier genommen ?
Sufian
@ Sufian Nein, ich habe diese Antwort nicht gesehen, sondern nur den gleichen Weg gefunden. Aber dieser Code sieht definitiv hübscher aus als meiner :)
maff91
2
Ich habe versucht, dies mit dem FirebaseRecyclerAdapter zu kombinieren, es hat nicht funktioniert. Dies liegt daran, dass der FirebaseRecyclerAdapter nicht den onChange (des AdapterDataObservers) aufruft, sondern die onItem * -Methoden. Damit es funktioniert, fügen Sie Folgendes hinzu: `Override public void onItemRangeRemoved (int positionStart, int itemCount) {// the check} Überschreibe public void onItemRangeMoved (int fromPosition, int toPosition, int itemCount) {// the check} // etc ..`
FrankkieNL
1
Warum überprüfen Sie, ob der Adapter im Beobachter null ist? Wenn der Adapter null ist, sollte der Beobachter niemals aufgerufen werden.
Stimsoni
16
ugh, ich muss sagen, das ist nur klassische Google BS. Ich muss dies nur implementieren, um eine leere Ansicht hinzuzufügen. Es sollte in ihrem cr @ psh1t SDK enthalten sein! Um Himmels Willen!
Neon Warge
62

Hier ist eine Lösung, bei der nur ein benutzerdefinierter Adapter mit einem anderen Ansichtstyp für die leere Situation verwendet wird.

public class EventAdapter extends 
    RecyclerView.Adapter<EventAdapter.ViewHolder> {

    private static final int VIEW_TYPE_EVENT = 0;
    private static final int VIEW_TYPE_DATE = 1;
    private static final int VIEW_TYPE_EMPTY = 2;

    private ArrayList items;

    public EventAdapter(ArrayList items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        if(items.size() == 0){
            return 1;
        }else {
            return items.size();
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (items.size() == 0) {
            return VIEW_TYPE_EMPTY;
        }else{
            Object item = items.get(position);
            if (item instanceof Event) {
                return VIEW_TYPE_EVENT;
            } else {
                return VIEW_TYPE_DATE;
            }
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        ViewHolder vh;
        if (viewType == VIEW_TYPE_EVENT) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event, parent, false);
            vh = new ViewHolderEvent(v);
        } else if (viewType == VIEW_TYPE_DATE) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_date, parent, false);
            vh = new ViewHolderDate(v);
        } else {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_empty, parent, false);
            vh = new ViewHolder(v);
        }

        return vh;
    }

    @Override
    public void onBindViewHolder(EventAdapter.ViewHolder viewHolder, 
                                 final int position) {
        int viewType = getItemViewType(position);
        if (viewType == VIEW_TYPE_EVENT) {
            //...
        } else if (viewType == VIEW_TYPE_DATE) {
            //...
        } else if (viewType == VIEW_TYPE_EMPTY) {
            //...
        }
    }

    public static class ViewHolder extends ParentViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    public static class ViewHolderDate extends ViewHolder {

        public ViewHolderDate(View v) {
            super(v);
        }
    }

    public static class ViewHolderEvent extends ViewHolder {

        public ViewHolderEvent(View v) {
            super(v);
        }
    }

}
radu122
quelle
3
@ sfk92fksdf Warum ist es fehleranfällig? Dies ist die Art und Weise, wie Sie mehrere Ansichtstypen in recyclerview
MSpeed
@billynomates, weil getItemCount()sollte ein Oneliner sein, aber hier haben wir eine nicht triviale ifmit etwas versteckter Logik. Diese Logik erscheint auch unangenehm getItemViewTypeund Gott weiß, wo sonst
Alexey Andronov
3
Es muss nicht eine Zeile sein. Wenn Sie mehrere Ansichtstypen haben, können Sie dies auf diese Weise tun.
MSpeed
@ sfk92fksdf Was meinst du mit versteckter Logik? Der Code ist genau dort oben ▲
radu122
1
Meiner Meinung nach sollte diese Antwort die akzeptierte Antwort sein. Es ist wirklich der Weg, um mit mehreren Ansichtstypen umzugehen
Ivo Beckers
45

Ich benutze ViewSwitcher

<ViewSwitcher
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/switcher"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <TextView android:id="@+id/text_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/list_empty"
        android:gravity="center"
        />

</ViewSwitcher>

Im Code überprüfen Sie den Cursor / Datensatz und wechseln die Ansichten.

void showItems(Cursor items) {
    if (items.size() > 0) {

        mAdapter.switchCursor(items);

        if (R.id.list == mListSwitcher.getNextView().getId()) {
            mListSwitcher.showNext();
        }
    } else if (R.id.text_empty == mListSwitcher.getNextView().getId()) {
        mListSwitcher.showNext();
    }
}

Sie können auch Animationen einstellen, wenn Sie dies mit ein paar Codezeilen wünschen

mListSwitcher.setInAnimation(slide_in_left);
mListSwitcher.setOutAnimation(slide_out_right);
wnc_21
quelle
Es ist wirklich neu
Zhang Xiang
Saubere Lösung.
Ojonugwa Jude Ochalifu
30

Da ist Kevins Antwort nicht vollständig.
Dies ist die korrektere Antwort, wenn Sie RecyclerAdapters verwenden notifyItemInsertedund notifyItemRemovedden Datensatz aktualisieren. Siehe die Kotlin-Version, die ein anderer Benutzer unten hinzugefügt hat.

Java:

mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {

    @Override
    public void onChanged() {
        super.onChanged();
        checkEmpty();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        checkEmpty();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        super.onItemRangeRemoved(positionStart, itemCount);
        checkEmpty();
    }

    void checkEmpty() {
        mEmptyView.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
    }
});

Kotlin

adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    override fun onChanged() {
        super.onChanged()
        checkEmpty()
    }

    override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
        super.onItemRangeInserted(positionStart, itemCount)
        checkEmpty()
    }

    override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
        super.onItemRangeRemoved(positionStart, itemCount)
        checkEmpty()
    }

    fun checkEmpty() {
        empty_view.visibility = (if (adapter.itemCount == 0) View.VISIBLE else View.GONE)
    }
})
Wonsuc
quelle
2
Dies ist eine großartige Lösung. Ich schlage eine Bearbeitung vor, die die Kotlin-Version enthält.
Jungledev
Dies scheint gut zu sein, funktioniert aber möglicherweise nicht, wenn Sie swapAdapter () oder sogar setAdapter () mehrmals aufrufen.
Glaucus
Ich denke ist die eleganteste Lösung!
Android-Enthusiast
Gute Arbeit. Vergessen Sie nicht, die RecyclerView auszublenden, wenn Sie das Etikett
anzeigen
18

RVEmptyObserver

Anstatt eine benutzerdefinierte zu verwenden RecyclerView, ist das Erweitern von AdapterDataObservereine einfachere Lösung, mit der Sie eine benutzerdefinierte festlegen können View, die angezeigt wird, wenn die Liste keine Elemente enthält:

Anwendungsbeispiel:

RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView)
rvAdapter.registerAdapterDataObserver(observer);

Klasse:

public class RVEmptyObserver extends RecyclerView.AdapterDataObserver {
    private View emptyView;
    private RecyclerView recyclerView;

    public RVEmptyObserver(RecyclerView rv, View ev) {
        this.recyclerView = rv;
        this.emptyView    = ev;
        checkIfEmpty();
    }

    private void checkIfEmpty() {
        if (emptyView != null && recyclerView.getAdapter() != null) {
            boolean emptyViewVisible = recyclerView.getAdapter().getItemCount() == 0;
            emptyView.setVisibility(emptyViewVisible ? View.VISIBLE : View.GONE);
            recyclerView.setVisibility(emptyViewVisible ? View.GONE : View.VISIBLE);
        }
    }

    public void onChanged() { checkIfEmpty(); }
    public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); }
    public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); }
}
Sheharyar
quelle
Ich glaube , dass es schneller laufen , wenn Sie nicht rufen checkIfEmpty()in onChanged()undonItemRangeInserted()
Geekarist
Ich stimme zu (und das habe ich persönlich getan), aber dies sollte nur ein Ausgangspunkt für Menschen sein, die mit diesem Problem konfrontiert sind.
Sheharyar
9

getItemViewTypeÜberprüfen Sie auf dem Adapter , ob der Adapter 0 Elemente enthält, und geben Sie in diesem Fall einen anderen viewType zurück.

onCreateViewHolderÜberprüfen Sie dann bei Ihrer Überprüfung, ob der viewType derjenige ist, den Sie zuvor zurückgegeben haben, und blasen Sie eine andere Ansicht auf. In diesem Fall eine Layoutdatei mit dieser Textansicht

BEARBEITEN

Wenn dies immer noch nicht funktioniert, können Sie die Größe der Ansicht programmgesteuert wie folgt festlegen:

Point size = new Point();
((WindowManager)itemView.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

Und dann, wenn Sie Ihre Ansicht aufblasen, rufen Sie an:

inflatedView.getLayoutParams().height = size.y;
inflatedView.getLayoutParams().width = size.x;
Pedro Oliveira
quelle
Klingt gut. Allerdings komme ich nicht in , onCreateViewHolderwenn meine Daten - Set ist nulloder leer , da ich zurückkehren müssen , um 0in getItemCount.
JJD
Dann gib 1 zurück, wenn es null ist :)
Pedro Oliveira
Funktioniert etwas. Danke dir. - Kleinere Details sind, dass ich das leere Listenelement in der Recycler-Ansicht nicht vertikal zentrieren kann . Die Textansicht bleibt am oberen Bildschirmrand, obwohl ich sie festgelegt habe, android:layout_gravity="center"und android:layout_height="match_parent"für die übergeordnete Recycler-Ansicht.
JJD
Dies hängt vom Layout der verwendeten Zeile ab. Es sollte auf seiner Höhe mit match_parent übereinstimmen. Wenn Sie es immer noch nicht zum Laufen bringen können, fügen Sie einen globalen Layout-Listener hinzu und stellen Sie die Layout-Parameter so ein, dass sie dieselbe Höhe und Breite wie der Bildschirm haben.
Pedro Oliveira
Alle Container (Aktivität, Fragment, lineares Layout, Recycler-Ansicht, Textansicht) sind auf eingestellt android:layout_height="match_parent". Die Zeile wird jedoch nicht auf die Bildschirmhöhe erweitert.
JJD
8

Hier ist meine Klasse zum Anzeigen der leeren Ansicht, zum Wiederholen der Ansicht (wenn das Laden der API fehlgeschlagen ist) und zum Laden des Fortschritts für RecyclerView

public class RecyclerViewEmptyRetryGroup extends RelativeLayout {
    private RecyclerView mRecyclerView;
    private LinearLayout mEmptyView;
    private LinearLayout mRetryView;
    private ProgressBar mProgressBar;
    private OnRetryClick mOnRetryClick;

    public RecyclerViewEmptyRetryGroup(Context context) {
        this(context, null);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        if (child.getId() == R.id.recyclerView) {
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            return;
        }
        if (child.getId() == R.id.layout_empty) {
            mEmptyView = (LinearLayout) findViewById(R.id.layout_empty);
            return;
        }
        if (child.getId() == R.id.layout_retry) {
            mRetryView = (LinearLayout) findViewById(R.id.layout_retry);
            mRetryView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRetryView.setVisibility(View.GONE);
                    mOnRetryClick.onRetry();
                }
            });
            return;
        }
        if (child.getId() == R.id.progress_bar) {
            mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        }
    }

    public void loading() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.VISIBLE);
    }

    public void empty() {
        mEmptyView.setVisibility(View.VISIBLE);
        mRetryView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void retry() {
        mRetryView.setVisibility(View.VISIBLE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void success() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    public void setOnRetryClick(OnRetryClick onRetryClick) {
        mOnRetryClick = onRetryClick;
    }

    public interface OnRetryClick {
        void onRetry();
    }
}

activity_xml

<...RecyclerViewEmptyRetryGroup
        android:id="@+id/recyclerViewEmptyRetryGroup">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"/>

        <LinearLayout
            android:id="@+id/layout_empty">
            ...
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_retry">
            ...
        </LinearLayout>

        <ProgressBar
            android:id="@+id/progress_bar"/>

</...RecyclerViewEmptyRetryGroup>

Geben Sie hier die Bildbeschreibung ein

Die Quelle ist hier https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry

Phan Van Linh
quelle
7

Verwenden Sie AdapterDataObserver in der benutzerdefinierten RecyclerView

Kotlin:

RecyclerViewEnum.kt

enum class RecyclerViewEnum {
    LOADING,
    NORMAL,
    EMPTY_STATE
}

RecyclerViewEmptyLoadingSupport.kt

class RecyclerViewEmptyLoadingSupport : RecyclerView {

    var stateView: RecyclerViewEnum? = RecyclerViewEnum.LOADING
        set(value) {
            field = value
            setState()
        }
    var emptyStateView: View? = null
    var loadingStateView: View? = null


    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}


    private val dataObserver = object : AdapterDataObserver() {
        override fun onChanged() {
            onChangeState()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            super.onItemRangeRemoved(positionStart, itemCount)
            onChangeState()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            super.onItemRangeInserted(positionStart, itemCount)
            onChangeState()
        }
    }


    override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
        super.setAdapter(adapter)
        adapter?.registerAdapterDataObserver(dataObserver)
        dataObserver.onChanged()
    }


    fun onChangeState() {
        if (adapter?.itemCount == 0) {
            emptyStateView?.visibility = View.VISIBLE
            loadingStateView?.visibility = View.GONE
            this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
        } else {
            emptyStateView?.visibility = View.GONE
            loadingStateView?.visibility = View.GONE
            this@RecyclerViewEmptyLoadingSupport.visibility = View.VISIBLE
        }
    }

    private fun setState() {

        when (this.stateView) {
            RecyclerViewEnum.LOADING -> {
                loadingStateView?.visibility = View.VISIBLE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
                emptyStateView?.visibility = View.GONE
            }

            RecyclerViewEnum.NORMAL -> {
                loadingStateView?.visibility = View.GONE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.VISIBLE
                emptyStateView?.visibility = View.GONE
            }
            RecyclerViewEnum.EMPTY_STATE -> {
                loadingStateView?.visibility = View.GONE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
                emptyStateView?.visibility = View.VISIBLE
            }
        }
    }
}

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/emptyView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/emptyLabelTv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="empty" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/loadingView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_gravity="center"
            android:indeterminate="true"
            android:theme="@style/progressBarBlue" />
    </LinearLayout>

    <com.peeyade.components.recyclerView.RecyclerViewEmptyLoadingSupport
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

Verwenden Sie in der Aktivität Folgendes:

recyclerView?.apply {
        layoutManager = GridLayoutManager(context, 2)
        emptyStateView = emptyView
        loadingStateView = loadingView
        adapter = adapterGrid
    }

    // you can set LoadingView or emptyView manual
    recyclerView.stateView = RecyclerViewEnum.EMPTY_STATE
    recyclerView.stateView = RecyclerViewEnum.LOADING
Rasoul Miri
quelle
1
Wenn Sie sich für diese Lösung entscheiden, achten Sie darauf, dass die Verwendung von Aufzählungen aufgrund der erhöhten Speicherzuweisungsgröße nicht ausreicht. Verwenden Sie Integer oder String je nach Kontext mit entsprechenden StringDefoder IntDefAnmerkungen
Andrii Artamonov
2

Ich fügte hinzu RecyclerViewund Alternative ImageViewzu RelativeLayout:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/no_active_jobs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_active_jobs" />

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

</RelativeLayout>

und dann in Adapter:

@Override
public int getItemCount() {
    if (mOrders.size() == 0) {
        mRecyclerView.setVisibility(View.INVISIBLE);
    } else {
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    return mOrders.size();
}
YTerle
quelle
7
wie man mRecyclerViewin dieAdapter
Basheer AL-MOMANI
2
Es ist kein gutes Entwurfsmuster, den Adapter an eine bestimmte Aktivität zu binden. Es ist besser, das über Schnittstellen zu tun
ruX
1
Dies führt auch zu einem Layoutüberzug, der nicht empfohlen wird und einige Leistungsprobleme verursachen kann. Siehe youtube.com/watch?v=T52v50r-JfE für weitere Details
Felipe Conde
1

Eine weitere Möglichkeit besteht darin, addOnChildAttachStateChangeListenerdie Handles zu verwenden, in denen untergeordnete Ansichten angezeigt / ausgeblendet werden RecyclerView.

recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onChildViewDetachedFromWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.VISIBLE);
            }
        });
versteckt
quelle
0

Nur für den Fall, dass Sie mit einem FirebaseRecyclerAdapter arbeiten, funktioniert dieser Beitrag als Charm https://stackoverflow.com/a/39058636/6507009

Ritterwürfel
quelle
7
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Anh Pham