Эквивалент ListView.setEmptyView в RecyclerView
на RecyclerView
, Я хочу установить пустой вид, который будет отображаться, когда адаптер пуст. Есть ли эквивалент ListView.setEmptyView()
?
13 ответов:
новая функция привязки данных вы также можете достичь этого в вашем макете напрямую:
<TextView android:text="No data to display." android:visibility="@{dataset.size() > 0 ? View.GONE : View.VISIBLE}" />
в этом случае вам просто нужно добавить переменную и импорт в раздел данных вашего XML:
<data> <import type="android.view.View"/> <variable name="dataset" type="java.util.List<java.lang.String>" /> </data>
вот класс, похожий на @dragon born, но более полный. На основе в этом суть.
public class EmptyRecyclerView extends RecyclerView { private View emptyView; final private AdapterDataObserver observer = new AdapterDataObserver() { @Override public void onChanged() { checkIfEmpty(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); } }; public EmptyRecyclerView(Context context) { super(context); } public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } void checkIfEmpty() { if (emptyView != null && getAdapter() != null) { final boolean emptyViewVisible = getAdapter().getItemCount() == 0; emptyView.setVisibility(emptyViewVisible ? VISIBLE : GONE); setVisibility(emptyViewVisible ? GONE : VISIBLE); } } @Override public void setAdapter(Adapter adapter) { final Adapter oldAdapter = getAdapter(); if (oldAdapter != null) { oldAdapter.unregisterAdapterDataObserver(observer); } super.setAdapter(adapter); if (adapter != null) { adapter.registerAdapterDataObserver(observer); } checkIfEmpty(); } public void setEmptyView(View emptyView) { this.emptyView = emptyView; checkIfEmpty(); } }
решения этой ссылке выглядит идеально. Он использует viewType, чтобы определить, когда показывать emptyView. Нет необходимости создавать пользовательские RecyclerView
добавление кода из приведенной выше ссылки:
package com.example.androidsampleproject; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class RecyclerViewActivity extends Activity { RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view); recyclerView = (RecyclerView) findViewById(R.id.myList); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new MyAdapter()); } private class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<String> dataList = new ArrayList<String>(); public class EmptyViewHolder extends RecyclerView.ViewHolder { public EmptyViewHolder(View itemView) { super(itemView); } } public class ViewHolder extends RecyclerView.ViewHolder { TextView data; public ViewHolder(View v) { super(v); data = (TextView) v.findViewById(R.id.data_view); } } @Override public int getItemCount() { return dataList.size() > 0 ? dataList.size() : 1; } @Override public int getItemViewType(int position) { if (dataList.size() == 0) { return EMPTY_VIEW; } return super.getItemViewType(position); } @Override public void onBindViewHolder(RecyclerView.ViewHolder vho, final int pos) { if (vho instanceof ViewHolder) { ViewHolder vh = (ViewHolder) vho; String pi = dataList.get(pos); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; if (viewType == EMPTY_VIEW) { v = LayoutInflater.from(parent.getContext()).inflate(R.layout.empty_view, parent, false); EmptyViewHolder evh = new EmptyViewHolder(v); return evh; } v = LayoutInflater.from(parent.getContext()).inflate(R.layout.data_row, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } private static final int EMPTY_VIEW = 10; } }
Я бы просто предпочел простое решение, как,
У вашего RecyclerView внутри FrameLayout или RelativeLayout с TextView или другим представлением с отображением пустого сообщения данных с видимостью по умолчанию, а затем в классе адаптера примените логику
здесь, у меня есть один TextView с сообщением нет данных
@Override public int getItemCount() { textViewNoData.setVisibility(data.size() > 0 ? View.GONE : View.VISIBLE); return data.size(); }
моя версия, основанная на https://gist.github.com/adelnizamutdinov/31c8f054d1af4588dc5c
public class EmptyRecyclerView extends RecyclerView { @Nullable private View emptyView; public EmptyRecyclerView(Context context) { super(context); } public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private void checkIfEmpty() { if (emptyView != null && getAdapter() != null) { emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE); } } private final AdapterDataObserver observer = new AdapterDataObserver() { @Override public void onChanged() { checkIfEmpty(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); } }; @Override public void setAdapter(@Nullable Adapter adapter) { final Adapter oldAdapter = getAdapter(); if (oldAdapter != null) { oldAdapter.unregisterAdapterDataObserver(observer); } super.setAdapter(adapter); if (adapter != null) { adapter.registerAdapterDataObserver(observer); } checkIfEmpty(); } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); if (null != emptyView && (visibility == GONE || visibility == INVISIBLE)) { emptyView.setVisibility(GONE); } else { checkIfEmpty(); } } public void setEmptyView(@Nullable View emptyView) { this.emptyView = emptyView; checkIfEmpty(); } }
Я бы предпочел реализовать эту функцию в Recycler.Адаптер
в переопределенном методе getItemCount введите пустые коды проверки:
@Override public int getItemCount() { if(data.size() == 0) listIsEmtpy(); return data.size(); }
Если вы хотите поддерживать больше состояний, таких как состояние загрузки, состояние ошибки, то вы можете проверить https://github.com/rockerhieu/rv-adapter-states. в противном случае поддержка пустого представления может быть легко реализована с помощью
RecyclerViewAdapterWrapper
от (https://github.com/rockerhieu/rv-adapter). главным преимуществом такого подхода является то, что вы можете легко поддерживать пустой вид без изменения логики существующего адаптера:public class StatesRecyclerViewAdapter extends RecyclerViewAdapterWrapper { private final View vEmptyView; @IntDef({STATE_NORMAL, STATE_EMPTY}) @Retention(RetentionPolicy.SOURCE) public @interface State { } public static final int STATE_NORMAL = 0; public static final int STATE_EMPTY = 2; public static final int TYPE_EMPTY = 1001; @State private int state = STATE_NORMAL; public StatesRecyclerViewAdapter(@NonNull RecyclerView.Adapter wrapped, @Nullable View emptyView) { super(wrapped); this.vEmptyView = emptyView; } @State public int getState() { return state; } public void setState(@State int state) { this.state = state; getWrappedAdapter().notifyDataSetChanged(); notifyDataSetChanged(); } @Override public int getItemCount() { switch (state) { case STATE_EMPTY: return 1; } return super.getItemCount(); } @Override public int getItemViewType(int position) { switch (state) { case STATE_EMPTY: return TYPE_EMPTY; } return super.getItemViewType(position); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case TYPE_EMPTY: return new SimpleViewHolder(vEmptyView); } return super.onCreateViewHolder(parent, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (state) { case STATE_EMPTY: onBindEmptyViewHolder(holder, position); break; default: super.onBindViewHolder(holder, position); break; } } public void onBindEmptyViewHolder(RecyclerView.ViewHolder holder, int position) { } public static class SimpleViewHolder extends RecyclerView.ViewHolder { public SimpleViewHolder(View itemView) { super(itemView); } } }
использование:
Adapter adapter = originalAdapter(); StatesRecyclerViewAdapter statesRecyclerViewAdapter = new StatesRecyclerViewAdapter(adapter, emptyView); rv.setAdapter(endlessRecyclerViewAdapter); // Change the states of the adapter statesRecyclerViewAdapter.setState(StatesRecyclerViewAdapter.STATE_EMPTY); statesRecyclerViewAdapter.setState(StatesRecyclerViewAdapter.STATE_NORMAL);
я исправил это:
Создан макет layout_recyclerview_with_emptytext.XML-файл.
Создан EmptyViewRecyclerView.java
---------EmptyViewRecyclerView emptyRecyclerView = (EmptyViewRecyclerView) findViewById (R. id. emptyRecyclerViewLayout);
emptyRecyclerView.addAdapter (mPrayerCollectionRecyclerViewAdapter, " нет молитвы для выбранной категории.");layout_recyclerview_with_emptytext.XML-файл
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/switcher" > <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.ninestars.views.CustomFontTextView android:id="@+id/recyclerViewEmptyTextView" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Empty Text" android:layout_gravity="center" android:gravity="center" android:textStyle="bold" /> </merge>
EmptyViewRecyclerView.java
public class EmptyViewRecyclerView extends ViewSwitcher { private RecyclerView mRecyclerView; private CustomFontTextView mRecyclerViewExptyTextView; public EmptyViewRecyclerView(Context context) { super(context); initView(context); } public EmptyViewRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } private void initView(Context context) { LayoutInflater.from(context).inflate(R.layout.layout_recyclerview_with_emptytext, this, true); mRecyclerViewExptyTextView = (CustomFontTextView) findViewById(R.id.recyclerViewEmptyTextView); mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView); mRecyclerView.setLayoutManager(new LinearLayoutManager(context)); } public void addAdapter(final RecyclerView.Adapter<?> adapter) { mRecyclerView.setAdapter(adapter); adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { super.onChanged(); if(adapter.getItemCount() > 0) { if (R.id.recyclerView == getNextView().getId()) { showNext(); } } else { if (R.id.recyclerViewEmptyTextView == getNextView().getId()) { showNext(); } } } }); } public void addAdapter(final RecyclerView.Adapter<?> adapter, String emptyTextMsg) { addAdapter(adapter); setEmptyText(emptyTextMsg); } public RecyclerView getRecyclerView() { return mRecyclerView; } public void setEmptyText(String emptyTextMsg) { mRecyclerViewExptyTextView.setText(emptyTextMsg); } }
попробовать
RVEmptyObserver
:это реализация
AdapterDataObserver
это позволяет вам просто установитьView
как пустой макет по умолчанию для вашегоRecylerView
. Таким образом, вместо использования пользовательскогоRecyclerView
и сделать вашу жизнь сложнее, вы можете легко использовать его с существующим кодом:
Пример Использования:
RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView) rvAdapter.registerAdapterDataObserver(observer);
вы можете код и пример использования в реальном приложении здесь.
класс:
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(); } }
public class EmptyRecyclerView extends RecyclerView { @Nullable View emptyView; public EmptyRecyclerView(Context context) { super(context); } public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } void checkIfEmpty() { if (emptyView != null) { emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE); } } final @NotNull AdapterDataObserver observer = new AdapterDataObserver() { @Override public void onChanged() { super.onChanged(); checkIfEmpty(); } }; @Override public void setAdapter(@Nullable Adapter adapter) { final Adapter oldAdapter = getAdapter(); if (oldAdapter != null) { oldAdapter.unregisterAdapterDataObserver(observer); } super.setAdapter(adapter); if (adapter != null) { adapter.registerAdapterDataObserver(observer); } } public void setEmptyView(@Nullable View emptyView) { this.emptyView = emptyView; checkIfEmpty(); } }
что-то вроде этого может помочь
Я думаю, что это более полно как ErrorView & EmptyView https://gist.github.com/henrytao-me/2f7f113fb5f2a59987e7
вы можете просто нарисовать текст на
RecyclerView
, когда он пуст. Следующий пользовательский подкласс поддерживаетempty
,failed
,loading
иoffline
режима. Для успешной компиляции добавьтеrecyclerView_stateText
цвет для ваших ресурсов./** * {@code RecyclerView} that supports loading and empty states. */ public final class SupportRecyclerView extends RecyclerView { public enum State { NORMAL, LOADING, EMPTY, FAILED, OFFLINE } public SupportRecyclerView(@NonNull Context context) { super(context); setUp(context); } public SupportRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); setUp(context); } public SupportRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setUp(context); } private Paint textPaint; private Rect textBounds; private PointF textOrigin; private void setUp(Context c) { textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(ContextCompat.getColor(c, R.color.recyclerView_stateText)); textBounds = new Rect(); textOrigin = new PointF(); } private State state; public State state() { return state; } public void setState(State newState) { state = newState; calculateLayout(getWidth(), getHeight()); invalidate(); } private String loadingText = "Loading..."; public void setLoadingText(@StringRes int resId) { loadingText = getResources().getString(resId); } private String emptyText = "Empty"; public void setEmptyText(@StringRes int resId) { emptyText = getResources().getString(resId); } private String failedText = "Failed"; public void setFailedText(@StringRes int resId) { failedText = getResources().getString(resId); } private String offlineText = "Offline"; public void setOfflineText(@StringRes int resId) { offlineText = getResources().getString(resId); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); String s = stringForCurrentState(); if (s == null) return; canvas.drawText(s, textOrigin.x, textOrigin.y, textPaint); } private void calculateLayout(int w, int h) { String s = stringForCurrentState(); if (s == null) return; textPaint.setTextSize(.1f * w); textPaint.getTextBounds(s, 0, s.length(), textBounds); textOrigin.set( w / 2f - textBounds.width() / 2f - textBounds.left, h / 2f - textBounds.height() / 2f - textBounds.top); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); calculateLayout(w, h); } private String stringForCurrentState() { if (state == State.EMPTY) return emptyText; else if (state == State.LOADING) return loadingText; else if (state == State.FAILED) return failedText; else if (state == State.OFFLINE) return offlineText; else return null; } }