viewholder update recyclerview from diffutil data como change actualizar android android-5.0-lollipop android-recyclerview

android - update - notifyDataSetChanged no funciona en RecyclerView



refresh recyclerview from viewholder (5)

Obtengo datos del servidor y luego los analizo y los almaceno en una Lista. Estoy usando esta lista para el adaptador RecyclerView. Estoy usando fragmentos.

Estoy usando un Nexus 5 con KitKat. Estoy usando la biblioteca de soporte para esto. ¿Esto hará una diferencia?

Aquí está mi código: (usando datos ficticios para la pregunta)

Variables del miembro:

List<Business> mBusinesses = new ArrayList<Business>(); RecyclerView recyclerView; RecyclerView.LayoutManager mLayoutManager; BusinessAdapter mBusinessAdapter;

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

Después de obtener datos del servidor, se llama a parseResponse() .

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

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

Pero no consigo los datos mostrados en la vista. ¿Qué estoy haciendo mal?

Aquí está mi registro,

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

No consigo ningún registro después de esto. ¿No debería llamarse de nuevo a getItemCount() en el adaptador?


Borre su antiguo modelo de vista y configure los nuevos datos en el adaptador y llame a notifyDataSetChanged()


En su parseResponse() está creando una nueva instancia de la clase BusinessAdapter , pero en realidad no la está utilizando en ningún lugar, por lo que su RecyclerView no sabe que existe la nueva instancia.

O bien necesitas:

  • Llame nuevamente a recyclerView.setAdapter(mBusinessAdapter) para actualizar la referencia del adaptador de RecyclerView para que apunte a la nueva
  • O simplemente elimine mBusinessAdapter = new BusinessAdapter(mBusinesses); Para continuar utilizando el adaptador existente. Como no ha cambiado la referencia de mBusinesses , el adaptador seguirá utilizando esa lista de arreglos y debería actualizarse correctamente cuando llame a notifyDataSetChanged() .

Pruebe este método:

List<Business> mBusinesses2 = mBusinesses; mBusinesses.clear(); mBusinesses.addAll(mBusinesses2); //and do the notification

un poco de tiempo, pero debería funcionar.


Solo para complementar las otras respuestas, ya que no creo que nadie haya mencionado esto aquí: notifyDataSetChanged() debe ejecutarse en el hilo principal (otros métodos de notify<Something> de RecyclerView.Adapter también, por supuesto)

De lo que recojo, ya que tiene los procedimientos de análisis y la llamada a notifyDataSetChanged() en el mismo bloque, lo está llamando desde un subproceso de trabajo o está haciendo un análisis JSON en el hilo principal (que también es un no -No como estoy seguro que lo sabes. Así que la forma correcta sería:

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

}

Lo extraño es que no creo que se obtengan indicaciones sobre el tema principal del subproceso desde el IDE o los registros de tiempo de ejecución. Esto es solo por mis observaciones personales: si llamo a notifyDataSetChanged() desde un subproceso de trabajo, no obtengo lo obligatorio. Solo el subproceso original que creó una jerarquía de vistas puede tocar su mensaje de vistas o algo así, simplemente falla silenciosamente (y en mi caso, una llamada fuera del hilo principal puede incluso impedir que las llamadas al hilo principal funcionen correctamente, probablemente debido a algún tipo de condición de carrera)

Además, ni la referencia de la aplicación RecyclerView.Adapter ni la guía de desarrollo oficial relevante mencionan explícitamente el requisito del hilo principal en este momento (el momento es 2017) y ninguna de las reglas de inspección de pelusas de Android Studio parece preocuparse por este problema.

Pero, aquí hay una explicación de esto por el propio autor.


Tuve el mismo problema Acabo de resolverlo con la declaración del adapter público antes de onCreate of class.

PostAdapter postAdapter;

después de esto

postAdapter = new PostAdapter(getActivity(), posts); recList.setAdapter(postAdapter);

por fin he llamado:

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

Que esto te ayude.