¿Cómo detectar si RecyclerView está vacío?
Tengo un RecyclerView obtener datos JSON externos analizados desde un servidor. Funciona bien, sin embargo la tarea asíncrona Volley en JSON a veces toma un tiempo y cuando lo hace el fragmento muestra una vista vacía en blanco.
¿Cómo puedo crear una prueba para comprobar si la vista está vacía y mostrar un msg si es? Traté de comprobar:
- Recyclerview no cambia de tamaño después de girar la pantalla
- CheckBox en RecyclerView mantiene la comprobación de diferentes elementos
- ¿Cómo obtener la vista de niño de RecyclerView?
- Decoración de artículos de RecyclerView
- Conjunto de datos de cambio RecyclerView
if (recyclerView == null) if (jsonList == null) if (adapter.getItemCount() == 0) if (bundle == null)
Pero esas pruebas no hacen nada o muestran el mensaje de error cada vez, incluso si el RecyclerView no está vacía.
Este es el código en el fragmento:
public void onViewCreated(View view, Bundle savedInstanceState) { LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.supportsPredictiveItemAnimations(); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setClickable(true); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); if (novaAdapter.getItemCount() != 0) { recyclerView.setAdapter(novaAdapter); }else{ pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Retrieving data from Server"); pDialog.show(); } super.onViewCreated(view, savedInstanceState);
Y el método en el adaptador:
@Override public int getItemCount() { return (null != novaList ? novaList.size() : 0); }
La forma en que ahora es el diálogo de progreso siempre se ejecuta sin importar si la vista está vacía o no.
UPDATE: Aquí está el código del adaptador:
public class NovaAdapter extends RecyclerView.Adapter<NovaListRowHolder> { ArrayList<HashMap<String, String>> novaList = new ArrayList<HashMap<String, String>>(); public static final String STATUS = "status"; public static final String NAME = "name"; public static final String ID = "id"; private Context mContext; public NovaAdapter(Context context, ArrayList<HashMap<String, String>> novaList) { this.novaList = novaList; this.mContext = context; } @Override public NovaListRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.instances_list, null); NovaListRowHolder mh = new NovaListRowHolder(v); return mh; } @Override public void onBindViewHolder(NovaListRowHolder novaListRowHolder, int i) { HashMap<String, String> e = novaList.get(i); novaListRowHolder.name.setText(e.get(NAME)); novaListRowHolder.status.setText(e.get(STATUS)); novaListRowHolder.setId(e.get(ID)); } @Override public int getItemCount() { return (null != novaList ? novaList.size() : 0); }class NovaListRowHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ protected TextView name; protected TextView status; protected String id; public String getId() { return id; } public void setId(String id) { this.id = id; } public NovaListRowHolder(View view) { super(view); view.setOnClickListener(this); this.name = (TextView) view.findViewById(R.id.nameInstance); this.status = (TextView) view.findViewById(R.id.statusInstance); } public void onClick(View view){ Dialog dialog = new Dialog(view.getContext()); dialog.setContentView(R.layout.instances_listdetail); dialog.setTitle("Details " + name.getText() + " " + getPosition()); dialog.show(); }
UPDATE2:
He actualizado otra clase que es prácticamente la misma que la anterior con una interfaz de devolución de llamada sin embargo ahora el recyclerView muestra durante 1 segundo y luego se queda en blanco. El cuadro de diálogo ni siquiera se muestra. Aquí está el código:
public class SubnetsFragment extends Fragment implements OnJSONLoaded{ /** * The fragment argument representing the section number for this * fragment. */ private static final String ARG_SECTION_NUMBER = "section_number"; private OnFragmentInteractionListener mListener; public ArrayList<HashMap<String, String>> jsonList; public RecyclerView recyclerView; public ProgressDialog pDialog; /** * Returns a new instance of this fragment for the given section * number. */ public static SubnetsFragment newInstance(int sectionNumber) { SubnetsFragment fragment = new SubnetsFragment(); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } public SubnetsFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle extras = getArguments(); Serializable parsedList = extras.getSerializable("SubnetsParsed"); jsonList = (ArrayList<HashMap<String, String>>)parsedList; if (extras == null){ AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); alert.setTitle("Token Expired"); alert.setMessage("Authentication Token expired! Please login again.") .setNeutralButton("Connect", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Intent intent = new Intent(getActivity(), Login.class); startActivity(intent); getActivity().finish(); getFragmentManager().beginTransaction().remove(SubnetsFragment.this).commit(); } }); AlertDialog alertDialog = alert.create(); alertDialog.show(); } View rootView = inflater.inflate(R.layout.fragment_subnets, container, false); recyclerView = (RecyclerView)rootView.findViewById(R.id.subnetsRV); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.supportsPredictiveItemAnimations(); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setClickable(true); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); onJsonLoaded(jsonList); } @Override public void onJsonLoaded(ArrayList<HashMap<String, String>> list) { SubnetsParser.setOnJSONLoadedListener(new OnJSONLoaded() { @Override public void onJsonLoaded(ArrayList<HashMap<String, String>> list) { if (list.size() != 0){ SubnetsAdapter subnetsAdapter = new SubnetsAdapter(getActivity(),jsonList); recyclerView.setAdapter(subnetsAdapter); }else { pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Retrieving data from Server"); pDialog.show(); } } }); } @Override public void onAttach(Activity activity) { super.onAttach(activity); ((Stackerz) activity).onSectionAttached( getArguments().getInt(ARG_SECTION_NUMBER)); //try { // mListener = (OnFragmentInteractionListener) activity; //} catch (ClassCastException e) { // throw new ClassCastException(activity.toString() // + " must implement OnFragmentInteractionListener"); //} } @Override public void onDetach() { super.onDetach(); mListener = null; } /** * This interface must be implemented by activities that contain this * fragment to allow an interaction in this fragment to be communicated * to the activity and potentially other fragments contained in that * activity. * <p> * See the Android Training lesson <a href= * "http://developer.android.com/training/basics/fragments/communicating.html" * >Communicating with Other Fragments</a> for more information. */ public interface OnFragmentInteractionListener { // TODO: Update argument type and name public void onFragmentInteraction(Uri uri); } }
Y esta es la clase JSON Parser:
public class SubnetsParser extends Activity{ public static final String NAME = "name"; public static final String GW = "gw"; public static final String CIDR = "cidr"; public static final String ID = "id"; public String authToken; public String neutronURL; public static SubnetsParser parser = null; public static OnJSONLoaded mListener; public static void setOnJSONLoadedListener(OnJSONLoaded listener) { mListener = listener; } public interface OnJSONLoaded { void onJsonLoaded(ArrayList<HashMap<String, String>> list); } public static SubnetsParser shared(){ if (parser == null){ parser = new SubnetsParser(); } return parser ; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public static ArrayList<HashMap<String, String>> parseJSON(String subnetsJSON){ ArrayList<HashMap<String, String>> jsonList = new ArrayList<HashMap<String, String>>(); try { Subnets subnets = new Subnets(); JSONObject subnet = new JSONObject(subnetsJSON); JSONArray subnetobj = subnet.getJSONArray("subnets"); for (int i = 0; i < subnetobj.length(); i++) { JSONObject objsrv = subnetobj.getJSONObject(i); subnets.setName(objsrv.getString("name")); subnets.setGw(objsrv.getString("gateway_ip")); subnets.setCidr(objsrv.getString("cidr")); subnets.setId(objsrv.getString("id")); HashMap<String, String> map = new HashMap<String, String>(); map.put(NAME, subnets.getName()); map.put(GW, subnets.getGw()); map.put(CIDR, subnets.getCidr()); map.put(ID, subnets.getId()); jsonList.add(map); } } catch (JSONException e) { Log.d("ErrorInitJSON", e.toString()); e.printStackTrace(); } Collections.sort(jsonList, new Comparator<HashMap<String, String>>() { @Override public int compare(HashMap<String, String> lhs, HashMap<String, String> rhs) { return (lhs.get("name")).compareToIgnoreCase(rhs.get("name")); } }); if (mListener != null) { mListener.onJsonLoaded(jsonList); } return jsonList; } }
- Detectar clic en RecyclerView fuera de los elementos
- LinearLayoutManager setOrientation después de rotación del teléfono no funciona
- Recyclerview lag en desplazamiento debido a los anuncios de Admob
- RecyclerVer elementoClickListener en Kotlin
- ¿Cómo puedo hacer que RecyclerView actualice su diseño?
- Recyclerview notifyDataSetChanged (); no funciona
- No se puede usar getLayoutPosition () o getAdapterPosition () en RecyclerView
- Elemento de centrado de Android en RecyclerView
Puede comprobar si está vacía ejecutando:
if (adapter.getItemCount() == 0)
Si no está funcionando, significa que no ha reemplazado el getItemCount en su adaptador! Así que asegúrese de que está sobrepasado:
@Override public int getItemCount() { return mDataSet.size(); // Where mDataSet is the list of your items }
Actualización: Así que, basándose en su actualización, así es como puede proceder. En mi opinión, sólo necesita una devolución de llamada. Está comprobando si la lista está vacía en su onViewCreated. En su lugar, debe utilizar una devolución de llamada. Haz algo como eso:
public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.supportsPredictiveItemAnimations(); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setClickable(true); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Retrieving data from Server"); pDialog.show(); }
En la clase que está utilizando para llenar su jsonList, asumo que un asynctask o una clase separada agregue esto:
private OnJsonLoaded mListener; public void setOnJsonLoadedListener(OnJsonLoaded listener) { mListener = listener; } public interface OnJsonLoaded { void onJsonLoaded(ArrayList<HashMap<String, String>> list); }
Ahora, en el asynctask que puebla ur jsonLise o cuando el parser json termine su trabajo, llame al oyente:
if (mListener != null) { mListener.onJsonLoaded(jsonList); }
En su fragmento (el que tiene NovaAdapter novaAdapter = nuevo NovaAdapter (getActivity (), jsonList) y su recyclerview) agregue la implementación de la interfaz:
classThatParseJson.setOnJsonLoadedListener(new OnJsonLoaded() { @Override public void onJsonLoaded(ArrayList<HashMap<String, String>> list) { if (list.size() != 0) { NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); recyclerView.setAdapter(novaAdapter); } else { // Show something like a dialog that the json list is 0 or do whatever you want... here the jsonlist have a count of 0 so it's empty! } } });
El código puede contener errores, lo escribí a mano sin usar IDE así que tal vez usted tiene que arreglar las cosas pequeñas, pero la lógica es bastante clara!
Actualización basada en su actualización 2:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_subnets, container, false); recyclerView = (RecyclerView)rootView.findViewById(R.id.subnetsRV); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.supportsPredictiveItemAnimations(); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setClickable(true); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); // start json parser here instead of passing to fragment as a bundle SubnetsParser.parseJSON(yourparams); } @Override public void onJsonLoaded(ArrayList<HashMap<String, String>> list) { SubnetsParser.setOnJSONLoadedListener(new OnJSONLoaded() { @Override public void onJsonLoaded(ArrayList<HashMap<String, String>> list) { if (list.size() != 0){ SubnetsAdapter subnetsAdapter = new SubnetsAdapter(getActivity(),jsonList); recyclerView.setAdapter(subnetsAdapter); }else { //pDialog = new ProgressDialog(getActivity()); //pDialog.setMessage("Retrieving data from Server"); //pDialog.show(); //Instead of a progressdialog, put here a dialog informing that the list is empty! } } }); }
Cómo se describe en https://developer.android.com/training/material/lists-cards.html
El método overriden getItemCount()
es invocado por el gestor de diseño. Este es el fragmento:
// Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return mDataset.length; }
Así que para detectar si el recyclerView
está vacío, debe solicitarlo a su LayoutManager
. Ejemplo:
if( mLayoutManager.getItemCount() == 0 ){ //Do something }
Trato de getItemCount()
de mi Adapter
pero esto devuelve 0, no sé por qué es …
- ¿Cómo mantener una imagen en una actividad de Android durante un cambio de orientación?
- Android: mediante el inicio de sesión de Google para obtener token de acceso