Android ListView no se actualiza después de notifyDataSetChanged


Mi código ListFragment

public class ItemFragment extends ListFragment {

    private DatabaseHandler dbHelper;
    private static final String TITLE = "Items";
    private static final String LOG_TAG = "debugger";
    private ItemAdapter adapter;
    private List<Item> items;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.item_fragment_list, container, false);        
        return view;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
        getActivity().setTitle(TITLE);
        dbHelper = new DatabaseHandler(getActivity());
        items = dbHelper.getItems(); 
        adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
        this.setListAdapter(adapter);

    }



    @Override
    public void onResume() {
        super.onResume();
        items.clear();
        items = dbHelper.getItems(); //reload the items from database
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        if(dbHelper != null) { //item is edited
            Item item = (Item) this.getListAdapter().getItem(position);
            Intent intent = new Intent(getActivity(), AddItemActivity.class);
            intent.putExtra(IntentConstants.ITEM, item);
            startActivity(intent);
        }
    }
}

Mi ListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Pero esto no refresca el ListView. Incluso después de reiniciar la aplicación, los elementos actualizados no se muestran. Mi ItemAdapter se extiende BaseAdapter

public class ItemAdapter extends BaseAdapter{

    private LayoutInflater inflater;
    private List<Item> items;
    private Context context;

    public ProjectListItemAdapter(Context context, List<Item> items) {
        super();
        inflater = LayoutInflater.from(context);
        this.context = context;
        this.items = items;

    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewHolder holder = null;
        if(convertView == null) {
            holder = new ItemViewHolder();
            convertView = inflater.inflate(R.layout.list_item, parent,false);
            holder.itemName = (TextView) convertView.findViewById(R.id.topText);
            holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
            convertView.setTag(holder);
        } else {
            holder = (ItemViewHolder) convertView.getTag();
        }
        holder.itemName.setText("Name: " + items.get(position).getName());
        holder.itemLocation.setText("Location: " + items.get(position).getLocation());
        if(position % 2 == 0) {                                                                                 
            convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
        } else {    
            convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
        }
        return convertView;
    }

    private static class ItemViewHolder {
        TextView itemName;
        TextView itemLocation;
    }
}

¿Puede alguien ayudar por favor?

Author: edwoollard, 2013-01-24

11 answers

Mira tu método onResume en ItemFragment:

@Override
public void onResume() {
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); // reload the items from database
    adapter.notifyDataSetChanged();
}

Lo que acaba de actualizar antes de llamar a notifyDataSetChanged() no es el campo del adaptador private List<Item> items; sino el campo idéntico declarado del fragmento. El adaptador todavía almacena una referencia a la lista de elementos que pasó cuando creó el adaptador (por ejemplo, en onCreate de fragment). La forma más corta (en el sentido del número de cambios) pero no elegante de hacer que su código se comporte como espera es simplemente reemplazar la línea:

    items = dbHelper.getItems(); // reload the items from database

Con

    items.addAll(dbHelper.getItems()); // reload the items from database

Una solución más elegante:

1) eliminar elementos private List<Item> items; de ItemFragment - necesitamos mantener la referencia a ellos solo en el adaptador

2) cambiar una vez creado a:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setHasOptionsMenu(true);
    getActivity().setTitle(TITLE);
    dbHelper = new DatabaseHandler(getActivity());
    adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
    setListAdapter(adapter);
}

3) añadir método en ÍtemAdapter:

public void swapItems(List<Item> items) {
    this.items = items;
    notifyDataSetChanged();
}

4) cambie su onResume a:

@Override
public void onResume() {
    super.onResume();
    adapter.swapItems(dbHelper.getItems());
}
 217
Author: Tomasz Gawel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-03 15:09:24

Está asignando elementos recargados a elementos de variables globales en onResume(), pero esto no se reflejará en la clase ItemAdapter, porque tiene su propia variable de instancia llamada 'items'.

Para actualizar ListView, agregue un refresh() en la clase ItemAdapter que acepte datos de lista, es decir, elementos

class ItemAdapter
{
    .....

    public void refresh(List<Item> items)
    {
        this.items = items;
        notifyDataSetChanged();
    } 
}

Actualizar onResume() con el siguiente código

@Override
public void onResume()
{
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); //reload the items from database
    **adapter.refresh(items);**
}
 21
Author: Santhosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-21 12:43:39

En onResume () cambiar esta línea

items = dbHelper.getItems(); //reload the items from database

A

items.addAll(dbHelper.getItems()); //reload the items from database

El problema es que nunca le estás diciendo a tu adaptador sobre la lista de nuevos elementos. Si no desea pasar una nueva lista a su adaptador (como parece que no), simplemente use items.addAll después de su clear(). Esto asegurará que está modificando la misma lista a la que el adaptador tiene una referencia.

 8
Author: Justin Breitfeller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-17 13:23:56

Si el adaptador ya está configurado, configurarlo de nuevo no actualizará la vista de lista. En su lugar, primero verifique si listview tiene un adaptador y luego llame al método apropiado.

Creo que no es una muy buena idea crear una nueva instancia del adaptador mientras se establece la vista de lista. En su lugar, cree un objeto.

BuildingAdapter adapter = new BuildingAdapter(context);

    if(getListView().getAdapter() == null){ //Adapter not set yet.
     setListAdapter(adapter);
    }
    else{ //Already has an adapter
    adapter.notifyDataSetChanged();
    }

También puede intentar ejecutar la lista de actualización en el hilo de interfaz de usuario:

activity.runOnUiThread(new Runnable() {         
        public void run() {
              //do your modifications here

              // for example    
              adapter.add(new Object());
              adapter.notifyDataSetChanged()  
        }
});
 4
Author: AlexGo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-24 14:58:50

Si desea actualizar su listview no importa si desea hacerlo en onResume(), onCreate() o en alguna otra función, lo primero que tiene que darse cuenta es que no necesitará crear una nueva instancia del adaptador, simplemente rellene las matrices con sus datos nuevamente. La idea es algo similar a esto :

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    myListView = (ListView) findViewById(R.id.my_list);

    titles = new ArrayList<String>()

    for(int i =0; i<20;i++){
        titles.add("Title "+i);
    }

    adapter = new MyListAdapter(this, titles);
    myListView.setAdapter(adapter);
}


@Override
public void onResume(){
    super.onResume();
    // first clear the items and populate the new items
    titles.clear();
    for(int i =0; i<20;i++){
        titles.add("New Title "+i);
    }
    adapter.notifySetDataChanged();
}

Así que dependiendo de esa respuesta deberías usar el mismo List<Item> en tu Fragment. En su primera inicialización de adaptador, llene su lista con los elementos y configure el adaptador a su listview. Después de eso, en cada cambio en sus elementos, debe borrar los valores del elemento principal List<Item> items y luego rellenarlo de nuevo con sus nuevos elementos y llamar a notifySetDataChanged();.

Así es como funciona : ).

 4
Author: hardartcore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-27 10:14:25

Una respuesta de AlexGo hizo el truco para mí:

getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
         messages.add(m);
         adapter.notifyDataSetChanged();
         getListView().setSelection(messages.size()-1);
        }
});

La actualización de la lista funcionó para mí antes cuando la actualización se activó desde un evento GUI, por lo tanto, estando en el subproceso de la interfaz de usuario.

Sin embargo, cuando actualizo la lista desde otro evento/subproceso, es decir, una llamada desde fuera de la aplicación, la actualización no estaría en el subproceso de la interfaz de usuario e ignoró la llamada a getListView. Llamar a la actualización con runOnUiThread como anteriormente hizo el truco para mí. ¡Gracias!!

 3
Author: user2996950,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-20 07:12:56

Prueba esto

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}
 3
Author: Gautami,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-22 11:31:30
adpter.notifyDataSetInvalidated();

Pruebe esto en onPause() método de la clase de actividad.

 2
Author: Som,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-30 09:55:59
adapter.setNotifyDataChanged()

Debería hacer el truco.

 1
Author: Hitman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-30 09:23:12

Intenta así:

this.notifyDataSetChanged();

En lugar de:

adapter.notifyDataSetChanged();

Tienes que notifyDataSetChanged() a la ListView no a la clase adapter.

 0
Author: Jachu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-30 08:15:54

Si su lista está contenida en el propio Adaptador, llamar a la función que actualiza la lista también debe llamar a notifyDataSetChanged().

Ejecutar esta función desde el subproceso de interfaz de usuario hizo el truco para mí:

La función refresh() dentro del Adaptador

public void refresh(){
    //manipulate list
    notifyDataSetChanged();
}

Luego, a su vez, ejecute esta función desde el Subproceso de interfaz de usuario

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});
 0
Author: Dévan Coetzee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 10:51:14