¿Cómo mostrar una vista vacía con RecyclerView?

Estoy acostumbrado a poner una vista especial dentro del archivo de diseño como se describe en la documentación ListActivity que se mostrará cuando no hay datos . Esta vista tiene la identificación "android:id/empty" .

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

Me pregunto cómo se puede hacer esto con el nuevo RecyclerView ?

En el mismo diseño donde se define el RecyclerView , agregue el 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" /> 

En la onCreate o la devolución de llamada adecuada, comprueba si el conjunto de datos que alimenta su RecyclerView está vacío. Si el conjunto de datos está vacío, el RecyclerView está vacío también. En ese caso, el mensaje aparece en la pantalla. Si no, cambia su visibilidad:

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

Para mis proyectos hice esta solución ( RecyclerView con el método setEmptyView ):

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

Y deberías usarla en lugar de la clase RecyclerView :

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

y

 RecyclerViewEmptySupport list = (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1); list.setLayoutManager(new LinearLayoutManager(context)); list.setEmptyView(rootView.findViewById(R.id.list_empty)); 

Esta es una solución que utiliza sólo un adaptador personalizado con un tipo de vista diferente para la situación vacía.

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

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

En el código que va a comprobar cursor / dataset y cambiar de vista.

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

También puede establecer animaciones si lo desea con un par de líneas de código

 mListSwitcher.setInAnimation(slide_in_left); mListSwitcher.setOutAnimation(slide_out_right); 

En su adaptador getItemViewType compruebe si el adaptador tiene 0 elementos y devuelve un tipo de vista diferente si es así.

A continuación, en su onCreateViewHolder compruebe si el viewType es el que ha devuelto anteriormente y infla una vista diferente. En este caso, un archivo de diseño con ese TextView

EDITAR

Si esto todavía no funciona, puede que desee establecer el tamaño de la vista de forma programática de la siguiente manera:

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

Y luego, cuando inflas tu llamada de vista:

 inflatedView.getLayoutParams().height = size.y; inflatedView.getLayoutParams().width = size.x; 

RVEmptyObserver

En lugar de utilizar un RecyclerView personalizado, la extensión de un AdapterDataObserver es una solución más sencilla que permite establecer una View personalizada que se muestra cuando no hay elementos en la lista:

Ejemplo de uso:

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

Clase:

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

Aquí está mi clase para mostrar la vista vacía, la vista de reintento (cuando la carga de api falló) y el progreso de carga de 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> 

Introduzca aquí la descripción de la imagen

La fuente está aquí https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry

He añadido RecyclerView y ImageView alternativo a 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> 

Y luego en Adapter :

 @Override public int getItemCount() { if (mOrders.size() == 0) { mRecyclerView.setVisibility(View.INVISIBLE); } else { mRecyclerView.setVisibility(View.VISIBLE); } return mOrders.size(); } 

Sólo en caso de que está trabajando con un FirebaseRecyclerAdapter esta publicación funciona como un encanto https://stackoverflow.com/a/39058636/6507009

  • Implementar un RecyclerView en un fragmento
  • ¿Que es mejor? NotifyDataSetChanged o notifyItemChanged en el bucle?
  • El desplazamiento automático se produce cuando Horizontal RecyclerView se coloca como un elemento en un RecyclerView vertical
  • ¿Cómo obtener la vista en RecyclerView.Adapter para Snackbar?
  • Uso de la vista de reciclaje con una base de datos
  • SetLayoutManager NullPointException en RecyclerView
  • La mejor manera de actualizar datos con un adaptador RecyclerView
  • Con RecyclerView, ¿Picasso sigue siendo necesario?
  • RecyclerView no recicla las vistas si el recuento de vistas es pequeño
  • ¿Cómo utilizo un GridLayoutAnimation en un RecyclerView?
  • RecyclerView wrap_content con GridLayoutManager
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.