2014-12-01 16 views
7

Ho un RecyclerView che riceve dati JSON esterni analizzati da un server. Funziona bene tuttavia il task Async di Volley su JSON a volte richiede un po 'di tempo e quando lo fa il frammento visualizza una vista vuota vuota.Come rilevare se RecyclerView è vuoto?

Come posso creare un test per verificare se la vista è vuota e visualizzare un messaggio, se lo è? Ho provato a controllare:

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

Ma questi test sia non faccio nulla oppure visualizzare il messaggio di errore ogni volta, anche se il RecyclerView non è vuota.

Questo è il codice sul frammento:

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

e il metodo sull'adattatore:

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

Il modo in cui è ora il dialogo sempre progressi eseguito non importa se la vista è vuota o no.

UPDATE: Ecco il codice dell'adattatore:

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:

ho aggiornato un'altra classe che è praticamente la stessa di quella di cui sopra con un'interfaccia di richiamata ma ora i display recyclerView per 1 secondo e poi diventa vuoto. Il dialogo non mostra nemmeno. Ecco il codice:

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

E questa è la classe 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; 

    } 


} 
+0

Perché non mostrano una progressbar ?? Questo è un uso molto comune con asynctask. – Mike

+0

Come posso utilizzare la barra di avanzamento con Volley? Il modo in cui la mia app è configurata per il volley viene utilizzata in una classe di background chiamata da un frammento – GITcommitEd

+0

RecyclerView in realtà un 'ViewGroup', hai mai provato" RecyclerView.getChildCount() == 0 "? – VinceStyling

risposta

7

È possibile controllare se è vuoto eseguendo:

if (adapter.getItemCount() == 0) 

Se non funziona ti significa rifugio 'Override the getItemCount sull'adattatore! in modo da assicurarsi che sia overrided:

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

Aggiornamento: Quindi, sulla base l'aggiornamento in questo modo si potrebbe procedere. Secondo me hai solo bisogno di una richiamata. Stai controllando se l'elenco è vuoto sul tuo onViewCreated. Dovresti, invece, usare un callback. Fare qualcosa di simile:

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

Nella classe che si utilizza per popolare il jsonList, suppongo che un AsyncTask o di una classe separata aggiungere questo:

private OnJsonLoaded mListener; 

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

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

ora, nel AsyncTask che popolano ur jsonLise o quando il parser jSON finire il suo lavoro, chiamare l'ascoltatore:

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

Nel vostro frammento (quello con NovaAdapter novaAdapter = new NovaAdapter (getActivity(), jsonList), e il vostro recyclervi ew) aggiungere l'implementazione dell'interfaccia:

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

il codice può containts errori, ho scritto a mano senza usare IDE quindi forse bisogna correggere le piccole cose, ma la logica è del tutto chiaro!

Aggiornamento in base alla tua 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! 
      } 
     } 
    }); 

} 
+0

Ho provato, ma ottengo: java.lang.NullPointerException: tentativo di richiamare il metodo virtuale 'int java.util.ArrayList.size()' su un oggetto nullo di riferimento presso app.Instances.NovaAdapter.getItemCount – GITcommitEd

+0

Ecco come era prima il metodo: @Override public int getItemCount() { return (null! = novaList? novaList.size(): 0); } – GITcommitEd

+0

quindi lasciatelo come era prima @Override public int getItemCount() {return (null! = NovaList? NovaList.size(): 0); Mostrami il tuo adattatore sorgente completo ad eccezione della porzione di viewhold – iGio90

3

Come è descritto in https://developer.android.com/training/material/lists-cards.html

Il metodo sovresposta getItemCount() viene richiamato dal gestore di layout. Questo è il frammento:

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

Quindi, per rilevare se il recyclerView è vuota è necessario richiedere al vostro LayoutManager. Esempio:

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

cerco di getItemCount() della mia Adapter ma questo restituisce 0, non so il motivo per cui è ...