Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect if RecyclerView is empty?

I have a RecyclerView getting external JSON data parsed from a server. It works fine however the Volley async task on JSON sometimes takes a while and when it does the fragment displays an empty blank view.

How can I create a test to check if the view is empty and display a msg if it is? I tried to check:

if (recyclerView == null)
if (jsonList == null)
if (adapter.getItemCount() == 0)
if (bundle == null)

But those tests either dont do anything or they display the error message every single time even if the RecyclerView is not empty.

This is the code on the fragment:

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

and the method on the Adapter:

@Override
public int getItemCount() {
    return (null != novaList ? novaList.size() : 0);
}

The way it is now the progress dialog always run no matter if the view is empty or not.

UPDATE: Here's the adapter code:

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:

I updated another class which is pretty much the same as the one above with a callback interface however now the recyclerView displays for 1 second and then goes blank. The dialog doesn't even show. Here's the code:

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

And this is the JSON Parser class:

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;

    }


}
like image 616
GITcommitEd Avatar asked Dec 01 '14 05:12

GITcommitEd


People also ask

What is Recyclerview setHasFixedSize?

setHasFixedSize(true) means the RecyclerView has children (items) that has fixed width and height.

What is Recyclerview getItemCount?

getItemCount() - returns The number of items currently available in adapter. This method returns the size of the collection that contains the items we want to display.


2 Answers

You can check if it's empty by running:

if (adapter.getItemCount() == 0)

If it's not working it means you haven't Override the getItemCount on your adapter! so make sure it's overrided:

@Override
public int getItemCount() {
    return mDataSet.size(); // Where mDataSet is the list of your items
}

Update: So based on your update this is how you could proceed. In my opinion you just need a callback. You are checking if the list is empty on your onViewCreated. You should, instead, use a callback. Do something like that:

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

In the class you are using to populate your jsonList, I assume an asynctask or a separate class add this:

private OnJsonLoaded mListener;

public void setOnJsonLoadedListener(OnJsonLoaded listener) {
    mListener = listener;
}

public interface OnJsonLoaded {
    void onJsonLoaded(ArrayList<HashMap<String, String>> list);
}

now, in the asynctask that populate ur jsonLise or when the json parser finish his job, call the listener:

if (mListener != null) {
    mListener.onJsonLoaded(jsonList);
}

In your fragment (the one with NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); and your recyclerview) add the interface implementation:

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

the code may containts errors, i written it by hand without using IDE so maybe you have to fix small things but the logic is quite clear!

Update based on your Update 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!
            }
        }
    });

}
like image 130
iGio90 Avatar answered Oct 12 '22 23:10

iGio90


How is described in https://developer.android.com/training/material/lists-cards.html

The overriden method getItemCount() is invoked by the layout manager. This is the snippet:

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.length;
}

So to detect if the recyclerView is empty you must request it to your LayoutManager. Example:

if( mLayoutManager.getItemCount() == 0 ){
    //Do something
}

I try to getItemCount() of my Adapter but this returns 0, I don't know why it is...

like image 24
Nomar Avatar answered Oct 13 '22 00:10

Nomar