Je reçois des données du serveur, puis les analyse et les stocke dans une liste. J'utilise cette liste pour l'adaptateur de RecyclerView. J'utilise des fragments.
J'utilise un Nexus 5 avec KitKat. J'utilise la bibliothèque de support pour cela. Cela fera-t-il une différence?
Voici mon code: (Utilisation de données factices pour la question)
Variables membres:
List<Business> mBusinesses = new ArrayList<Business>();
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
BusinessAdapter mBusinessAdapter;
Mon onCreateView()
:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Getting data from server
getBusinessesDataFromServer();
View view = inflater.inflate(R.layout.fragment_business_list,
container, false);
recyclerView = (RecyclerView) view
.findViewById(R.id.business_recycler_view);
recyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
mBusinessAdapter = new BusinessAdapter(mBusinesses);
recyclerView.setAdapter(mBusinessAdapter);
return view;
}
Après avoir obtenu les données du serveur, la fonction parseResponse()
est appelée.
protected void parseResponse(JSONArray response, String url) {
// insert dummy data for demo
mBusinesses.clear();
Business business;
business = new Business();
business.setName("Google");
business.setDescription("Google HeadQuaters");
mBusinesses.add(business);
business = new Business();
business.setName("Yahoo");
business.setDescription("Yahoo HeadQuaters");
mBusinesses.add(business);
business = new Business();
business.setName("Microsoft");
business.setDescription("Microsoft HeadQuaters");
mBusinesses.add(business);
Log.d(Const.DEBUG, "Dummy Data Inserted\nBusinesses Length: "
+ mBusinesses.size());
mBusinessAdapter = new BusinessAdapter(mBusinesses);
mBusinessAdapter.notifyDataSetChanged();
}
Mon BusinessAdapter:
public class BusinessAdapter extends
RecyclerView.Adapter<BusinessAdapter.ViewHolder> {
private List<Business> mBusinesses = new ArrayList<Business>();
// Provide a reference to the type of views that you are using
// (custom viewholder)
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextViewName;
public TextView mTextViewDescription;
public ImageView mImageViewLogo;
public ViewHolder(View v) {
super(v);
mTextViewName = (TextView) v
.findViewById(R.id.textView_company_name);
mTextViewDescription = (TextView) v
.findViewById(R.id.textView_company_description);
mImageViewLogo = (ImageView) v
.findViewById(R.id.imageView_company_logo);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public BusinessAdapter(List<Business> myBusinesses) {
Log.d(Const.DEBUG, "BusinessAdapter -> constructor");
mBusinesses = myBusinesses;
}
// Create new views (invoked by the layout manager)
@Override
public BusinessAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
Log.d(Const.DEBUG, "BusinessAdapter -> onCreateViewHolder()");
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_business_list, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
Log.d(Const.DEBUG, "BusinessAdapter -> onBindViewHolder()");
Business item = mBusinesses.get(position);
holder.mTextViewName.setText(item.getName());
holder.mTextViewDescription.setText(item.getDescription());
holder.mImageViewLogo.setImageResource(R.drawable.ic_launcher);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
Log.d(Const.DEBUG, "BusinessAdapter -> getItemCount()");
if (mBusinesses != null) {
Log.d(Const.DEBUG, "mBusinesses Count: " + mBusinesses.size());
return mBusinesses.size();
}
return 0;
}
}
Mais je ne reçois pas les données affichées dans la vue. Qu'est-ce que je fais mal?
Voici mon journal,
07-14 21:15:35.669: D/xxx(2259): Dummy Data Inserted
07-14 21:15:35.669: D/xxx(2259): Businesses Length: 3
07-14 21:26:26.969: D/xxx(2732): BusinessAdapter -> constructor
Je ne reçois pas de journaux après cela. getItemCount()
dans l'adaptateur ne devrait-il pas être appelé à nouveau?
Dans votre parseResponse()
, vous créez une nouvelle instance de la classe BusinessAdapter
, mais vous ne l'utilisez réellement nulle part. Votre RecyclerView
ne connaît donc pas la nouvelle instance. existe.
Vous devez soit:
recyclerView.setAdapter(mBusinessAdapter)
pour mettre à jour la référence de l'adaptateur de RecyclerView afin qu'elle pointe vers votre nouvellemBusinessAdapter = new BusinessAdapter(mBusinesses);
pour continuer à utiliser l'adaptateur existant. Puisque vous n'avez pas changé la référence mBusinesses
, l'adaptateur utilisera toujours cette liste de tableaux et devrait se mettre à jour correctement lorsque vous appelez notifyDataSetChanged()
.Essayez cette méthode:
List<Business> mBusinesses2 = mBusinesses;
mBusinesses.clear();
mBusinesses.addAll(mBusinesses2);
//and do the notification
cela prend un peu de temps, mais ça devrait marcher.
J'ai eu le même problème. Je viens de le résoudre en déclarant adapter
public avant onCreate
de la classe.
PostAdapter postAdapter;
après ça
postAdapter = new PostAdapter(getActivity(), posts);
recList.setAdapter(postAdapter);
enfin j'ai appelé:
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
// Display the size of your ArrayList
Log.i("TAG", "Size : " + posts.size());
progressBar.setVisibility(View.GONE);
postAdapter.notifyDataSetChanged();
}
Puisse cela vous aider.
Juste pour compléter les autres réponses car je pense que personne ne l’a mentionné ici: notifyDataSetChanged()
doit être exécuté sur le fil principal ( autres méthodes notify<Something>
de RecyclerView.Adapter
également, bien sûr)
D'après ce que je comprends, puisque vous avez les procédures d'analyse syntaxique et l'appel à notifyDataSetChanged()
dans le même bloc, vous l'appelez à partir d'un thread de travail ou vous effectuez une analyse JSON sur le thread principal (qui est également un non-non car je suis sûr que vous le savez). Donc, la bonne façon serait:
protected void parseResponse(JSONArray response, String url) {
// insert dummy data for demo
// <yadda yadda yadda>
mBusinessAdapter = new BusinessAdapter(mBusinesses);
// or just use recyclerView.post() or [Fragment]getView().post()
// instead, but make sure views haven't been destroyed while you were
// parsing
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
mBusinessAdapter.notifyDataSetChanged();
}
});
}
[~ # ~] ps [~ # ~] Ce qui est bizarre, c'est que je ne pense pas que vous obteniez des indications sur le sujet principal de IDE ou de run- journaux de temps. Cela vient juste de mes observations personnelles: si j'appelle notifyDataSetChanged()
depuis un thread de travail, je ne reçois pas l'obligation Seul le thread d'origine qui a créé une hiérarchie de vues peut toucher ses vues message ou quelque chose du genre - cela échoue simplement en silence (et dans mon cas, un appel hors du fil principal peut même empêcher le bon fonctionnement des appels suivants du fil principal, probablement à cause d'une condition de concurrence).
De plus, ni RecyclerView.Adapter api reference , ni l'officiel concerné dev guide ne mentionnent explicitement l'exigence de thread principal pour le moment (le moment est 2017) et aucun des Android Les règles d'inspection de Studio lint semblent concerner ce problème non plus.
Mais, voici une explication de cela par l'auteur lui-même
Effacez votre ancien modèle de vue, définissez les nouvelles données sur l'adaptateur et appelez notifyDataSetChanged()
.