Jak wyświetlić pusty widok za pomocą RecyclerView?

271

Jestem przyzwyczajony do umieszczania specjalnego widoku wewnątrz pliku układu, jak opisano w ListActivitydokumentacji, która ma być wyświetlana, gdy nie ma żadnych danych . Ten widok ma identyfikator "android:id/empty".

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

Zastanawiam się, jak można to zrobić z nowym RecyclerView?

JJD
źródło
2
Możesz użyć github.com/rockerhieu/rv-adapter-states , obsługuje on nie tylko widok pusty, ale także widok ładowania i widok błędów. I możesz go używać bez zmiany logiki istniejącego adaptera.
Hieu Rocker,
20
Fascynujące jest to, że nie ma prostej setEmptyView()metody przeciwko RecyclerView...
Subby
Oto moja odpowiedź, sprawdź ten link stackoverflow.com/a/58411351/5449220
Rakesh Verma
@ Subby, zgadzam się, ale setEmptyView()miał też wadę. Podczas ładowania danych nie chcieliśmy pokazywać pustego widoku ListView, ale tak się stało. Musieliśmy więc wprowadzić logikę. Obecnie używam stackoverflow.com/a/48049142/2914140 .
CoolMind,

Odpowiedzi:

305

W tym samym układzie, w którym zdefiniowano RecyclerView, dodaj 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" />

Przy onCreateodpowiednim oddzwonieniu sprawdzasz, czy zestaw danych, który karmi twój, RecyclerViewjest pusty. Jeśli zestaw danych jest pusty, to również RecyclerViewjest pusty. W takim przypadku komunikat pojawi się na ekranie. Jeśli nie, zmień jego widoczność:

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
źródło
11
Nie działa to dla mnie, ponieważ układ widoku recyklera jest zawyżony w tym fragmencie, podczas gdy decyzja, czy pokazać normalny układ przedmiotów, czy układ bez przedmiotów, jest podejmowana w adapterze.
JJD,
1
@JJD przy korzystaniu z Fragmentu obowiązuje to samo. Kiedy nadmuchujesz swój widok fragmentu, masz widok recyklera, a następnie inny układ, który zostałby użyty dla twojego pustego widoku. Zwykle w moich fragmentach mam następujące widoki: 1) widok recyklera, 2) pusty widok i 3) widok paska postępu. Zwykle przekazuję adapter bez względu na listę, ale na podstawie rozmiaru listy albo ukryję widok recyclingler i pokażę pustą lub odwrotnie. Jeśli widok recyklera ma pustą listę, wówczas nic z tym nie robi. Po prostu widzisz pusty widok.
Ray Hunter,
1
@stackex Weryfikacja musi odbywać się w wywołaniu zwrotnym onCreateView fragmentu lub onResume of Activity. W ten sposób weryfikacja zostanie wykonana bezpośrednio przed wyświetleniem ekranu użytkownikowi i będzie działać zarówno w przypadku zwiększania, jak i zmniejszania liczby wierszy w zestawie danych.
slellis
2
@Zapnologica Możesz to zrobić na odwrót: użyj magistrali Eventbus (takiej jak greenrobots Eventbus lub Otto), a następnie umieść adapter na postacie na magistrali zdarzeń, gdy zmieni się zestaw danych. W tym fragmencie wystarczy stworzyć metodę, której parametry odpowiadają temu, co adapter przekazuje do metody Eventbus.post, a następnie odpowiednio zmienić układ. Prostszym, ale także bardziej sprzężonym podejściem jest utworzenie interfejsu zwrotnego w adapterze, a po utworzeniu adaptera implementuje fragment.
AgentKnopf
2
W układzie otrzymuję wiele tagów głównych. Jak wygląda Twój plik pełnego układu?
proszek 366
192

Dla moich projektów Zrobiłem to rozwiązanie ( RecyclerViewz setEmptyViewmetody):

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

I powinieneś użyć go zamiast RecyclerViewklasy:

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

i

RecyclerViewEmptySupport list = 
    (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1);
list.setLayoutManager(new LinearLayoutManager(context));
list.setEmptyView(rootView.findViewById(R.id.list_empty));
maff91
źródło
2
Czy wziąłeś ten kod z tej odpowiedzi czy stąd ?
Sufian
@Sufian Nie, nie widziałem tej odpowiedzi, po prostu znalazłem tę samą drogę. Ale ten kod zdecydowanie wygląda ładniej niż mój :)
maff91
2
Próbowałem połączyć to z FirebaseRecyclerAdapter, ale to nie zadziałało. Wynika to z faktu, że FirebaseRecyclerAdapter nie wywołuje metody onChange (adapterDataObserver), a zamiast tego wywołuje metody onItem *. Aby to działało, dodaj: `Zastąp public void onItemRangeRemoved (int positionStart, int itemCount) {// the check} Zastąp public void onItemRangeMoved (int fromPosition, int toPosition, int itemCount) {// czek} // itd.
FrankkieNL,
1
Dlaczego sprawdzasz, czy adapter jest pusty w obserwatorze? jeśli adapter ma wartość NULL, obserwatora nigdy nie należy wywoływać.
Stimsoni,
16
Ugh, muszę powiedzieć, że to tylko klasyczny Google BS. Muszę to zaimplementować, aby dodać pusty widok? Powinien być zawarty w ich cr @ psh1t SDK! Na litość boską!
Neon Warge,
62

Oto rozwiązanie wykorzystujące tylko niestandardowy adapter z innym typem widoku dla pustej sytuacji.

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
źródło
3
@ sfk92fksdf Dlaczego jest podatny na błędy? W ten sposób powinieneś obsługiwać wiele typów widoków w widoku recyklera
MSpeed
@billynomates, ponieważ getItemCount()powinien być oneliner, ale tutaj mamy nietrywialne ifz ukrytą logiką. Ta logika pojawia się również niezręcznie getItemViewTypei Bóg wie, gdzie jeszcze
Aleksiej Andronow
3
To nie musi być jedna linia. Jeśli masz wiele typów widoków, możesz to zrobić w ten sposób.
MSpeed
@ sfk92fksdf Co rozumiesz przez ukrytą logikę? Kod jest tam na górze ▲
radu122
1
moim zdaniem odpowiedź ta powinna być odpowiedzią przyjętą. To naprawdę sposób na radzenie sobie z wieloma rodzajami widoków
Ivo Beckers
45

Używam 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>

w kodzie sprawdzisz kursor / zestaw danych i przełączysz widoki.

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

Możesz także ustawić animacje, jeśli chcesz, za pomocą kilku linii kodu

mListSwitcher.setInAnimation(slide_in_left);
mListSwitcher.setOutAnimation(slide_out_right);
wnc_21
źródło
To jest naprawdę powieść
Zhang Xiang
Czyste rozwiązanie.
Ojonugwa Jude Ochalifu
30

Ponieważ odpowiedź Kevina nie jest kompletna.
To jest bardziej poprawna odpowiedź, jeśli używasz RecyclerAdapter„s notifyItemInsertedoraznotifyItemRemoved do aktualizacji zbioru danych. Zobacz wersję Kotlin dodaną poniżej przez innego użytkownika.

Jawa:

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
źródło
2
To świetne rozwiązanie. Proponuję edycję, która obejmie wersję kotlin.
Jungledev
Wydaje się to dobre, ale może nie działać, jeśli wywołasz swapAdapter () lub nawet setAdapter () więcej niż raz.
Glaucus
Myślę, że to najbardziej eleganckie rozwiązanie!
entuzjasta Androida
Świetna robota. nie zapomnij ukryć RecyclerView, pokazując etykietę
Mr
18

RVEmptyObserver

Zamiast korzystania z niestandardowego RecyclerView, rozszerzenie AdapterDataObserverjest prostszym rozwiązaniem, które pozwala ustawić niestandardoweView wyświetlane, gdy nie ma żadnych elementów na liście:

Przykładowe użycie:

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

Klasa:

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
źródło
Wierzę, że to będzie działać szybciej, jeśli nie nazywamy checkIfEmpty()się onChanged()ionItemRangeInserted()
Geekarist
Zgadzam się (i to właśnie zrobiłem osobiście), ale miał to być punkt wyjścia dla osób borykających się z tym problemem.
Sheharyar
9

Na twoim adapterze getItemViewType sprawdź, czy karta ma 0 elementów, a jeśli tak, zwróć inny typ widoku.

Następnie onCreateViewHoldersprawdź, czy viewType jest tym, który zwróciłeś wcześniej, i nadmuchaj inny widok. W tym przypadku plik układu z tym TextView

EDYTOWAĆ

Jeśli to nadal nie działa, możesz ustawić rozmiar widoku programowo w następujący sposób:

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

A potem, gdy zawyżysz widok połączenia:

inflatedView.getLayoutParams().height = size.y;
inflatedView.getLayoutParams().width = size.x;
Pedro Oliveira
źródło
Brzmi dobrze. Jednak ja nie dostać się onCreateViewHolderkiedy mój zestaw danych jest nullalbo pusty, ponieważ muszę wrócić 0w getItemCount.
JJD
Następnie zwróć 1, jeśli jest pusty :)
Pedro Oliveira
Działa trochę. Dziękuję Ci. - Drobne szczegóły to to, że nie mogę uzyskać pustego elementu listy wyśrodkowanego pionowo w widoku recyklera. Widok tekstowy pozostaje jednak u góry ekranu, mimo że został ustawiony, android:layout_gravity="center"i android:layout_height="match_parent"dla widoku nadrzędnego recyklera.
JJD
To zależy od układu używanego wiersza. Powinien pasować do rodzica na swojej wysokości. Jeśli nadal nie możesz go uruchomić, dodaj globalny moduł nasłuchujący układu i ustaw jego parametry układu, aby miały tę samą wysokość i szerokość co ekran.
Pedro Oliveira,
Wszystkie pojemniki (aktywność, fragment, układ liniowy, widok recyklera, widok tekstu) są ustawione na android:layout_height="match_parent". Chociaż rząd nie rozwija się do wysokości ekranu.
JJD
8

Oto moja klasa do pokazywania pustego widoku, ponawiania widoku (w przypadku niepowodzenia ładowania interfejsu API) i postępu ładowania dla 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>

wprowadź opis zdjęcia tutaj

Źródło znajduje się tutaj https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry

Phan Van Linh
źródło
7

Użyj AdapterDataObserver w niestandardowym 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>

w działaniu użyj tego:

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
źródło
1
Jeśli wybierzesz to rozwiązanie, zwróć uwagę, że użycie wyliczeń jest niewystarczające ze względu na zwiększony rozmiar alokacji pamięci. Użyj liczby całkowitej lub ciągu w zależności od kontekstu z odpowiednimi StringDeflub IntDefadnotacjami
Andrii Artamonov,
2

Dodałem RecyclerViewi alternatywę ImageViewdla 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>

a następnie w Adapter:

@Override
public int getItemCount() {
    if (mOrders.size() == 0) {
        mRecyclerView.setVisibility(View.INVISIBLE);
    } else {
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    return mOrders.size();
}
YTerle
źródło
7
jak dotrzeć do mRecyclerViewwnętrzaAdapter
Basheer AL-MOMANI
2
Łączenie adaptera z konkretnym działaniem nie jest dobrym wzorcem projektowym. Lepiej to zrobić za pomocą interfejsów
ruX
1
Powoduje to również przerysowanie układu, co nie jest zalecane i może powodować pewne problemy z wydajnością. Zobacz youtube.com/watch?v=T52v50r-JfE więcej szczegółów
Felipe Conde
1

Jeszcze jednym sposobem jest użycie, w addOnChildAttachStateChangeListenerktórych uchwytach pojawiają się / znikają widoki dziecka 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);
            }
        });
ukrył
źródło
0

Wystarczy, że pracujesz z FirebaseRecyclerAdapter, ten post działa jako urok https://stackoverflow.com/a/39058636/6507009

knightcube
źródło
7
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Anh Pham