¿Cómo actualizar un Android ListView
después de agregar / eliminar datos dinámicos?
¿Cómo actualizar un Android ListView
después de agregar / eliminar datos dinámicos?
Respuestas:
Llame notifyDataSetChanged()
a su Adapter
objeto una vez que haya modificado los datos en ese adaptador.
Algunos detalles adicionales sobre cómo / cuándo llamar notifyDataSetChanged()
se pueden ver en el vídeo de Google I / O .
También puedes usar esto:
myListView.invalidateViews();
invalidateViews()
en los conjuntos de código fuente, mDataChanged = true;
así que no estoy seguro de si esto resulta en un mejor rendimiento en comparación connotifyDataSetChanged()
invalidate()
, invalidateViews()
, requestLayout()
, ... respuestas a esta pregunta.Lo correcto (y afortunadamente también marcado como respuesta correcta) es llamar notifyDataSetChanged()
a su adaptador .
Si las llamadas notifyDataSetChanged()
no funcionan, todos los métodos de diseño tampoco ayudarán. Créeme, ListView
se actualizó correctamente. Si no encuentra la diferencia, debe verificar de dónde provienen los datos en su adaptador.
Si esto es solo una colección que está guardando en la memoria, compruebe que realmente eliminó o agregó los elementos a la colección antes de llamar al notifyDataSetChanged()
.
Si está trabajando con una base de datos o backend de servicio, deberá llamar al método para recuperar la información nuevamente (o manipular los datos en la memoria) antes de llamar al notifyDataSetChanged()
.
La cuestión es que esto notifyDataSetChanged
solo funciona si el conjunto de datos ha cambiado. Así que ese es el lugar para buscar si no encuentra cambios. Depurar si es necesario.
Descubrí que trabajar con un adaptador que le permite administrar la colección, como un Adaptador de Base, funciona mejor. Algunos adaptadores como el ArrayAdapter ya administran su propia colección, lo que hace que sea más difícil llegar a la colección adecuada para actualizaciones. En realidad, es solo una capa adicional innecesaria de dificultad en la mayoría de los casos.
Es cierto que esto debe llamarse desde el hilo de la interfaz de usuario. Otras respuestas tienen ejemplos sobre cómo lograr esto. Sin embargo, esto solo es necesario si está trabajando en esta información desde fuera del hilo de la interfaz de usuario. Eso es de un servicio o un subproceso no UI. En casos simples, actualizará sus datos con solo hacer clic en un botón u otra actividad / fragmento. Aún así dentro del hilo de la interfaz de usuario. No es necesario que siempre aparezca esa runOnUiTrhead.
Se puede encontrar en https://github.com/hanscappelle/so-2250770.git . Simplemente clone y abra el proyecto en Android Studio (gradle). Este proyecto tiene un MainAcitivity construyendo un ListView
con todos los datos aleatorios. Esta lista se puede actualizar utilizando el menú de acciones.
La implementación del adaptador que creé para este ejemplo ModelObject expone la recopilació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 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();
}
}
Otra buena publicación sobre el poder de listViews se encuentra aquí: http://www.vogella.com/articles/AndroidListView/article.html
adapter.clear()
, y adapter.addAll(Array<T>)
antes de llamarnotifyDataSetChanged()
Llamada ejecutable cuando quieras:
runOnUiThread(run);
OnCreate()
, configura su hilo ejecutable:
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
runOnUiThread()
función.
run = new Runnable()
y simplemente le di a mi función un public void refreshUIThread()
método que modela su run()
método (que también funciona).
Tengo algunos problemas con la actualización dinámica de mi vista de lista.
Llame a notifyDataSetChanged () en su adaptador.
En este video de Google I / O se pueden ver algunos detalles adicionales sobre cómo / cuándo llamar a notifyDataSetChanged ().
notifyDataSetChanged () no funcionó correctamente en mi caso [llamé a notifyDataSetChanged de otra clase]. Solo en el caso de que edité el ListView en la Actividad en ejecución (Hilo). Ese video gracias a Christopher dio la pista final.
En mi segunda clase solía
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
para acceder a la actualización () desde mi Actividad. Esta actualización incluye
myAdapter.notifyDataSetChanged();
para decirle al adaptador que actualice la vista. Funcionó bien hasta donde puedo decir.
Si está utilizando SimpleCursorAdapter intente llamar a requery () en el objeto Cursor.
si aún no está satisfecho con el Refresco ListView, puede ver este fragmento, esto es para cargar listView desde DB, en realidad lo que tiene que hacer es simplemente volver a cargar ListView, después de realizar cualquier Operación CRUD No es la mejor manera de código, pero actualizará el ListView como desee.
Funciona para mí ... si encuentras una mejor solución, comparte ...
....... ...... hacer sus operaciones CRUD .. ...... ..... DBAdapter.open (); DBAdapter.insert_into_SingleList (); // Trae esos resultados DB_ y agrégalos a la lista como su contenido ... ls2.setAdapter (nuevo ArrayAdapter (DynTABSample.this, android.R.layout.simple_list_item_1, DBAdapter.DB_ListView)); DBAdapter.close ();
Las soluciones propuestas por las personas en esta publicación funcionan o no principalmente según la versión de Android de su dispositivo. Por ejemplo, para usar el método AddAll, debe poner android: minSdkVersion = "10" en su dispositivo Android.
Para resolver estas preguntas para todos los dispositivos, he creado mi propio método en mi adaptador y lo uso dentro del método de agregar y quitar hereda de ArrayAdapter que actualiza sus datos sin problemas.
Mi código: usando mi propia clase de datos RaceResult, usted usa 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 actualiza, y 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 obtener información detallada, consulte Android ListView: mantenga su posición de desplazamiento cuando actualice .
Solo use 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();
}
});
Debe usar un solo objeto de esa lista cuyos datos está inflando ListView
. Si la referencia es un cambio, entonces no notifyDataSetChanged()
funciona. Siempre que esté eliminando elementos de la vista de lista, también elimínelos de la lista que está utilizando, ya sea una ArrayList <> o Algo más, luego llame
notifyDataSetChanged()
al objeto de Su clase de adaptador.
Entonces, vea cómo lo administré en mi adaptador, vea a continuación
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 eliminar datos de la vista de lista, debe llamar refreshDrawableState()
. Aquí está el ejemplo:
final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
db.open();
db.deleteContact(arg3);
mListView.refreshDrawableState();
db.close();
y el deleteContact
método en DatabaseHelper
clase será algo parecido
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
No pude obtener notifyDataSetChanged () para trabajar en la actualización de mi SimpleAdapter, por lo que primero intenté eliminar todas las vistas que estaban adjuntas al diseño principal usando removeAllViews (), luego agregué ListView y funcionó, lo que me permitió actualizar el 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 actualizar la vista de lista (para ser una vista de lista expandible específica), por lo que si notifyDataSetChanged () no ayuda, puede intentar borrar su lista primero y agregarla nuevamente 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 ti.
mientras usa SimpleCursorAdapter puede llamar a changeCursor (newCursor) en el adaptador.
Era igual cuando, en un fragmento, quería llenar un ListView (en un único TextView) con la dirección mac de los dispositivos BLE escaneados durante algún tiempo.
Lo que hice fue esto:
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();
}
}
Luego, ListView comenzó a llenarse dinámicamente con la dirección mac de los dispositivos encontrados.
Creo que depende de lo que quieras decir con refrescar. ¿Quiere decir que la pantalla GUI debe actualizarse o quiere decir que las vistas secundarias deben actualizarse de manera que pueda llamar programáticamente a getChildAt (int) y obtener la vista correspondiente a lo que está en el Adaptador?
Si desea que se actualice la pantalla de la GUI, llame a notifyDataSetChanged () en el adaptador. La GUI se actualizará la próxima vez que se vuelva a dibujar.
Si desea poder llamar a getChildAt (int) y obtener una vista que refleje qué es lo que hay en el adaptador, llame a layoutChildren (). Esto hará que la vista secundaria se reconstruya a partir de los datos del adaptador.
Tenía una ArrayList que quería mostrar en una vista de lista. ArrayList contenía elementos de mysql. Anulé el método de actualización y en ese método usé tablelayout.removeAllViews (); y luego repitió el proceso para obtener datos nuevamente de la base de datos. Pero antes de eso, asegúrese de borrar su ArrayList o cualquier estructura de datos o, de lo contrario, se agregarán datos nuevos al anterior.
Si desea actualizar la vista de lista de la interfaz de usuario de un servicio, haga que el adaptador esté estático en su actividad Principal y haga esto:
@Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
Si utiliza las líneas de guía de Android y está utilizando el ContentProviders
para obtener datos Database
y lo está mostrando en el ListView
uso de CursorLoader
y CursorAdapters
, todos los cambios en 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 es necesario el trabajo extra.
Pero cuando no está utilizando todo esto, debe informar al adaptador cuando el conjunto de datos está cambiando. También debe volver a llenar / recargar su conjunto de datos (por ejemplo, la lista) y luego debe llamar notifyDataSetChanged()
al adaptador.
notifyDataSetChanged()
no funcionará si no hay cambios en el conjunto de datos. Aquí está el comentario sobre el método en docs-
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
Solo pude obtener notifyDataSetChanged solo al obtener nuevos datos del adaptador, luego restablecí el adaptador para la vista de lista y luego hice la llamada de esta manera:
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
en otra opción es el onWindowFocusChanged
método, pero seguro es sensible y necesita un poco de codificación adicional
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
}
Considere que ha pasado una lista a su adaptador.
Utilizar:
list.getAdapter().notifyDataSetChanged()
para actualizar tu lista.
Si hablé sobre mi escenario aquí, ninguna de las respuestas anteriores funcionará porque tuve una actividad que muestra una lista de valores de db junto con un botón Eliminar y cuando se presiona un botón Eliminar, quería eliminar ese elemento de la lista.
Lo bueno fue que no utilicé la vista de reciclador, sino una vista de lista simple y esa vista de lista inicializada en la clase de adaptador. Por lo tanto, llamar al notifyDataSetChanged()
no hará nada dentro de la clase de adaptador e incluso en la clase de actividad donde se inicializa el objeto del adaptador porque el método de eliminación estaba en la clase del adaptador.
Entonces, la solución fue eliminar el objeto del adaptador en el getView
método de clase de adaptador (para eliminar solo ese objeto específico, pero si desea eliminar todo, llame clear()
).
Para que te hagas una idea, cómo era mi código,
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: aquí remove()
y los getItem()
métodos se heredan de la clase Adaptador.
remove()
- para eliminar el elemento específico en el que se hace clicgetItem(position)
- es para obtener el elemento (aquí, ese es mi objeto de Word que he agregado a la lista) desde la posición en la que se hizo clic.Así es como configuro el adaptador para 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 hacer un nuevo Adaper y soltar el viejo:
myListView.setAdapter(new MyListAdapter(...));