Pregunta

Cómo actualizar un ListView Android después de añadir / borrar datos dinámicos?

¿Fue útil?

Solución

notifyDataSetChanged() en el objeto una vez que Adapter 've modificado los datos de ese adaptador.

Algunos detalles adicionales sobre cómo / cuándo llamar notifyDataSetChanged() se pueden ver en este vídeo Google I / O

Otros consejos

También se puede utilizar este:

myListView.invalidateViews();

Por favor ignorar todos los invalidate(), invalidateViews(), requestLayout(), ... respuestas a esta pregunta.

Lo que hay que hacer (y por suerte también marcado como respuesta correcta) es para llamar notifyDataSetChanged() de su adaptador .

Solución de problemas

Si llama notifyDataSetChanged() no funciona todos los métodos de diseño no servirá de nada. Créanme la ListView se actualiza correctamente. Si no puede encontrar la diferencia es necesario comprobar que los datos en su adaptador viene.

Si esto es sólo una colección que está manteniendo en jaque a la memoria que en realidad de baja o de añadido el artículo (s) a la colección antes de llamar al notifyDataSetChanged().

Si está trabajando con una base de datos o un servicio de back-end que tendrá que llamar al método para recuperar la información de nuevo (o manipular la memoria de datos) antes de llamar a la notifyDataSetChanged().

La cosa es que este notifyDataSetChanged sólo funciona si el conjunto de datos ha cambiado. Así que ese es el lugar para buscar si usted no encuentra cambios que vienen a través. Depurar si es necesario.

ArrayAdapter vs BaseAdapter

Me pareció que el trabajo con un adaptador que le permite administrar la colección, como un BaseAdapter funciona mejor. Algunos adaptadores como el ArrayAdapter ya gestionan su propia colección haciendo más difícil para llegar a la recolección adecuada para las actualizaciones. No deja de ser una capa adicional innecesaria de dificultad en la mayoría de los casos.

IU Tema

Es cierto que esto tiene que ser llamado desde el hilo de interfaz de usuario. Otras respuestas tienen ejemplos sobre cómo lograr esto. Sin embargo, esto sólo es necesario si se trabaja en esta información desde fuera del hilo de interfaz de usuario. Es decir, de un servicio o un hilo no la interfaz de usuario. En casos sencillos que va a actualizar sus datos desde un clic de botón o de otra actividad / fragmento. Así que aún dentro del hilo de interfaz de usuario. No hay necesidad para que aparezca siempre que runOnUiTrhead en.

Ejemplo Rápido Proyecto

Se puede encontrar en https://github.com/hanscappelle/so-2250770.git . Sólo clonar y abrir el proyecto en Android Studio (Gradle). Este proyecto tiene un MainAcitivity la construcción de una ListView con todos los datos aleatorios. Esta lista se puede actualizar mediante el menú de acciones.

La implementación del adaptador que he creado para este ejemplo ModelObject expone la colección de datos

public class MyListAdapter extends BaseAdapter {

    /**
     * this is our own collection of data, can be anything we 
     * want it to be as long as we get the abstract methods 
     * implemented using this data and work on this data 
     * (see getter) you should be fine
     */
    private List<ModelObject> mData;

    /**
     * our ctor for this adapter, we'll accept all the things 
     * we need here
     *
     * @param mData
     */
    public MyListAdapter(final Context context, final List<ModelObject> mData) {
        this.mData = mData;
        this.mContext = context;
    }

    public List<ModelObject> getData() {
        return mData;
    }

    // implement all abstract methods here
}

Código de la MainActivity

public class MainActivity extends Activity {

    private MyListAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView list = (ListView) findViewById(R.id.list);

        // create some dummy data here
        List<ModelObject> objects = getRandomData();
        // and put it into an adapter for the list
        mAdapter = new MyListAdapter(this, objects);
        list.setAdapter(mAdapter);

        // mAdapter is available in the helper methods below and the 
        // data will be updated based on action menu interactions

        // you could also keep the reference to the android ListView 
        // object instead and use the {@link ListView#getAdapter()} 
        // method instead. However you would have to cast that adapter 
        // to your own instance every time
    }

    /**
     * helper to show what happens when all data is new
     */
    private void reloadAllData(){
        // get new modified random data
        List<ModelObject> objects = getRandomData();
        // update data in our adapter
        mAdapter.getData().clear();
        mAdapter.getData().addAll(objects);
        // fire the event
        mAdapter.notifyDataSetChanged();
    }

    /**
     * helper to show how only changing properties of data 
     * elements also works
     */
    private void scrambleChecked(){
        Random random = new Random();
        // update data in our adapter, iterate all objects and 
        // resetting the checked option
        for( ModelObject mo : mAdapter.getData()) {
            mo.setChecked(random.nextBoolean());
        }
        // fire the event
        mAdapter.notifyDataSetChanged();
    }
}

Más información

se encuentra Otro buen puesto sobre el poder de listviews aquí: http: // www. vogella.com/articles/AndroidListView/article.html

Llamada ejecutable cuando lo desee:

runOnUiThread(run);

OnCreate(), se establece su hebra ejecutable:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};

Tengo algunos problemas con la actualización dinámica de mi vista de lista.

  

Llamada notifyDataSetChanged () en su adaptador.

     

Algunos detalles adicionales sobre cómo / cuándo llamar notifyDataSetChanged () se puede ver en este video de Google I / O.

notifyDataSetChanged () no funcionaba correctamente en mi caso [que se llama el notifyDataSetChanged de otra clase]. Sólo en el caso i editado el ListView en la Actividad de ejecución (Thread). Ese video gracias a Christopher dieron el toque final.

En mi segunda clase que solía

Runnable run = new Runnable(){
     public void run(){
         contactsActivity.update();
     }
};
contactsActivity.runOnUiThread(run);

para la actualización de los acces () de mi actividad. Esta actualización incluye

myAdapter.notifyDataSetChanged();

para decirle al adaptador para actualizar la vista. Trabajado bien en cuanto a lo que puedo decir.

Si está utilizando SimpleCursorAdapter intentar llamar requery () en el objeto Cursor.

si no está todavía satisfecho con Refresco ListView, se puede ver en este fragmento, esto es para cargar la vista de lista de la base de datos, en realidad lo que tiene que hacer es simplemente recargar la ListView, después de realizar cualquier operación CRUD No es una mejor forma de código, pero se actualizará el ListView como desee ..

Funciona para mí .... si u encontrar una mejor solución, por favor ... Comparte

.......
......
do your CRUD Operations..
......
.....
DBAdapter.open();
DBAdapter.insert_into_SingleList();
// Bring that DB_results and add it to list as its contents....
                                            ls2.setAdapter(new ArrayAdapter(DynTABSample.this,
    android.R.layout.simple_list_item_1,                        DBAdapter.DB_ListView));
                                            DBAdapter.close();

Las soluciones propuestas por la gente en este post funciona o no, dependiendo principalmente de la versión para Android de su dispositivo. Por ejemplo, para utilizar el método addAll usted tiene que poner android:. MinSdkVersion = "10" en su dispositivo Android

Para resolver esta pregunta para todos los dispositivos que he creado mi propia en el método en mi adaptador y usar en el interior del complemento y retire método hereda de ArrayAdapter que modificar sus datos sin problemas.

Mi Código: Usando mi propia RaceResult clase de datos, utiliza su propio modelo de datos

.

ResultGpRowAdapter.java

public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {

    Context context;
    int resource;
    List<RaceResult> data=null;

        public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
        super(context, resource, objects);

        this.context = context;
        this.resource = resource;
        this.data = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ........
        }

        //my own method to populate data           
        public void myAddAll(List<RaceResult> items) {

        for (RaceResult item:items){
            super.add(item);
        }
    }

ResultsGp.java

public class ResultsGp extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...........
    ...........
    ListView list = (ListView)findViewById(R.id.resultsGpList); 

    ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data

   list.setAdapter(adapter);

   .... 
   ....
   ....
   //LOAD a ArrayList<RaceResult> with data

   ArrayList<RaceResult> data = new ArrayList<RaceResult>();
   data.add(new RaceResult(....));
   data.add(new RaceResult(....));
   .......

   adapter.myAddAll(data); //Your list will be udpdated!!!

Si desea mantener su posición de desplazamiento cuando se actualizan, y usted puede hacer esto:

if (mEventListView.getAdapter() == null) {
    EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
    mEventListView.setAdapter(eventLogAdapter);
} else {
    ((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}

public void refill(List<EventLog> events) {
    mEvents.clear();
    mEvents.addAll(events);
    notifyDataSetChanged();
}

Para la información detallada, consulte ListView Android:. Mantener su posición de desplazamiento cuando actualice

Sólo tiene que utilizar myArrayList.remove(position); dentro de un oyente:

  myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
           myArrayList.remove(position);
           myArrayAdapter.notifyDataSetChanged();
        }
    });

Es necesario utilizar un solo objeto de esa lista de datos whoose está inflando el ListView. Si la referencia es cambiar a continuación notifyDataSetChanged() trabajo does't .Whenever Usted está eliminando elementos de la vista de lista también eliminarlos de la lista que está utilizando si es un ArrayList <> o algo más, entonces llamada notifyDataSetChanged() el objeto de su clase adaptador.

Así que aquí ver cómo me las arreglé en mi adaptador véase más adelante

public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{

private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;

public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
    this.context = context;
    this.dObj=dObj;
    completeList=new  ArrayList<CountryDataObject>();
    completeList.addAll(dObj);
    itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}

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

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

@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
    if(view==null){
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.states_inflator_layout, null);
        holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
        holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
        view.setTag(holder);
    }else{
        holder = (ViewHolder) view.getTag();
    }
    holder.textView.setText(dObj.get(position).getCountryName());
    holder.textView.setTypeface(itemFont);

    if(position==selectedPosition)
     {
         holder.checkImg.setImageResource(R.drawable.check);
     }
     else
     {
         holder.checkImg.setImageResource(R.drawable.uncheck);
     }
    return view;
}
private class ViewHolder{
    private TextView textView;
    private ImageView checkImg;
}

public void getFilter(String name) {
    dObj.clear();
    if(!name.equals("")){
    for (CountryDataObject item : completeList) {
        if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
            dObj.add(item);
        }
    }
    }
    else {
        dObj.addAll(completeList);
    }
    selectedPosition=-1;
    notifyDataSetChanged();
    notifyDataSetInvalidated(); 
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Registration reg=(Registration)context;
    selectedPosition=position;
    reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
    notifyDataSetChanged();
}
}

Después de borrar los datos de la vista de lista, usted tiene que llamar refreshDrawableState(). Aquí está el ejemplo:

final DatabaseHelper db = new DatabaseHelper (ActivityName.this);

db.open();

db.deleteContact(arg3);

mListView.refreshDrawableState();

db.close();

y deleteContact método en la clase DatabaseHelper será algo parece

public boolean deleteContact(long rowId) {

   return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;

}

Yo no era capaz de conseguir notifyDataSetChanged () para trabajar en la actualización de mi SimpleAdapter, así que en vez Probé primero la eliminación de todas las vistas que estaban conectados a la disposición de los padres usando removeAllViews (), a continuación, añadir el ListView, y que trabajaba, lo que me para actualizar la interfaz de usuario:

LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, 
                new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );

for (...) { 
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", name);
    map.put("dept", dept);
    list.add(map);
}

lv.setAdapter(adapter);
results.removeAllViews();     
results.addView(lv);

Para mí después de cambiar la información en la base de datos SQL nada podría refrescar la vista de lista (para ser vista de lista ampliable específico) por lo que si notifyDataSetChanged () no ayuda, se puede tratar de borrar la lista primero y agregarlo de nuevo después de esa llamada notifyDataSetChanged (). Por ejemplo

private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();

Espero que tenga sentido para usted.

durante el uso de SimpleCursorAdapter puede llamar changeCursor (newCursor) en el adaptador.

I era el mismo cuando, en un fragmento, quería rellenar un ListView (en una sola TextView) con la dirección MAC de los dispositivos de BLE escaneados durante algún tiempo.

Lo que hice fue lo siguiente:

public class Fragment01 extends android.support.v4.app.Fragment implements ...
{
    private ListView                listView;
    private ArrayAdapter<String>    arrayAdapter_string;

...

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    ...
    this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
    ...
    this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
    this.listView.setAdapter(this.arrayAdapter_string);
}


@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
    ...
    super.getActivity().runOnUiThread(new RefreshListView(device));
}


private class RefreshListView implements Runnable
{
    private BluetoothDevice bluetoothDevice;

    public RefreshListView(BluetoothDevice bluetoothDevice)
    {
        this.bluetoothDevice= bluetoothDevice;
    }

    @Override
    public void run()
    {
        Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
        Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
    }
}

A continuación, el ListView comenzaron a poblar dinámicamente con la dirección MAC de los dispositivos encontrados.

Creo que depende de lo que entendemos por actualización. ¿Quiere decir que la pantalla GUI debe ser refrescado, o ¿quiere decir que las opiniones del niño debe ser renovada de tal manera que se puede llamar mediante programación getChildAt (int) y obtener la vista que corresponde a lo que es en el adaptador.

Si desea que la pantalla GUI refrescado, a continuación, llamar notifyDataSetChanged () en el adaptador. La interfaz gráfica de usuario se actualizará la próxima vez que vuelve a dibujar.

Si usted quiere ser capaz de llamar a getChildAt (int) y obtener una vista que refleja lo que es lo que está en el adaptador, y luego llamar a layoutChildren (). Esto provocará que la vista del niño a ser reconstruido a partir de los datos del adaptador.

Yo tenía un ArrayList que he querido mostrar en una vista de lista. ArrayList contenía elementos de MySQL. I overrided OnRefresh método y en ese método que utiliza tablelayout.removeAllViews (); y luego se repite el proceso para la obtención de datos de nuevo desde la base de datos. Pero antes de eso, asegúrese de limpiar su ArrayList o lo que estructuran los datos o bien los nuevos datos se consiguen anexa a la anterior ..

Si desea actualizar la vista de lista de IU de un servicio, a continuación, hacer que el adaptador de estática en su actividad principal y hacer esto:

@Override
public void onDestroy() {
    if (MainActivity.isInFront == true) {
        if (MainActivity.adapter != null) {
            MainActivity.adapter.notifyDataSetChanged();
        }

        MainActivity.listView.setAdapter(MainActivity.adapter);
    }
}    

Si van por líneas de guía androide y está utilizando el ContentProviders obtener datos de Database y que estás viendo en la ListView utilizando el CursorLoader y CursorAdapters, entonces todos los cambios a los datos relacionados se reflejarán automáticamente en el ListView.

Su getContext().getContentResolver().notifyChange(uri, null); en el cursor en el ContentProvider será suficiente para reflejar los cambios .No necesita para el trabajo adicional alrededor.

Sin embargo, cuando no se está utilizando estos todos, entonces usted necesita para decirle al adaptador cuando el conjunto de datos está cambiando. También es necesario repoblar / recargar el conjunto de datos (lista decir) y luego es necesario llamar a notifyDataSetChanged() en el adaptador.

notifyDataSetChanged()wont trabajo si no hay cambios en la datset. Aquí está el comentario anterior en el método docs -

/**
 * Notifies the attached observers that the underlying data has been changed
 * and any View reflecting the data set should refresh itself.
 */

Yo sólo era capaz de conseguir notifyDataSetChanged solamente obteniendo nuevos datos del adaptador, a continuación, reiniciar el adaptador para la vista de lista, a continuación, hacer la llamada de esta manera:

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 

en otra opción es el método onWindowFocusChanged, pero seguro que es sensible y necesita algo de código adicional para quien se interesa

 override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)

       // some controls needed
        programList = usersDBHelper.readProgram(model.title!!)
        notesAdapter = DailyAdapter(this, programList)
        notesAdapter.notifyDataSetChanged()
        listview_act_daily.adapter = notesAdapter
    }

Tenga en cuenta que haya pasado una lista para su adaptador.
Uso:

list.getAdapter().notifyDataSetChanged()

para actualizar la lista.

Si hablé de mi situación aquí, no de respuestas anteriores no será trabajado porque no tenía actividad que mostrar lista de valores db junto con un botón de borrar y cuando se pulsa un botón de borrar, quería eliminar ese elemento de la lista .

Lo bueno fue, yo no utiliza vista reciclador pero un simple vista de lista y que la vista de lista inicializado en la clase adaptador. Por lo tanto, llamar a la notifyDataSetChanged() no va a hacer nada dentro de la clase de adaptador e incluso en la clase de actividad donde objeto adaptador está inicializado porque el método de eliminación estaba en la clase adaptador.

Por lo tanto, la solución fue eliminar el objeto del adaptador en el método getView clase adaptador (para eliminar sólo ese objeto específico, pero si usted quiere eliminar todo, llamar clear()).

Para que usted pueda obtener una idea, lo que fue mi código parecen,

public class WordAdapter extends ArrayAdapter<Word> {
  Context context;

  public WordAdapter(Activity context, ArrayList<Word> words) {}
    //.......

    @NonNull
    @Override
    public View getView(final int position, View convertView, ViewGroup group) {
      //.......
     ImageButton deleteBt = listItemView.findViewById(R.id.word_delete_bt);
     deleteBt.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             if (vocabDb.deleteWord(currentWord.id)) {
                //.....
             } else{
                //.....
             }
             remove(getItem(position)); // <---- here is the trick ---<
             //clear() // if you want to clear everything
         }
    });
 //....

Nota:. remove() aquí métodos y getItem() se heredan de la clase adaptador

  • remove() - para eliminar el elemento específico que se hace clic
  • getItem(position) - es conseguir que el elemento (en este caso, esa es mi objeto de Word que he añadido a la lista) desde la posición seleccionada.

Así es como me puse el adaptador a la vista de lista en la clase de actividad,

    ArrayList<Word> wordList = new ArrayList();
    WordAdapter adapter = new WordAdapter(this, wordList);

    ListView list_view = (ListView) findViewById(R.id.activity_view_words);
    list_view.setAdapter(adapter);

Lo más fácil es simplemente hacer un nuevo adaptador y soltar la anterior:

myListView.setAdapter(new MyListAdapter(...));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top