Como atualizar um Android ListView
depois de adicionar / excluir dados dinâmicos?
Como atualizar um Android ListView
depois de adicionar / excluir dados dinâmicos?
Respostas:
Chame notifyDataSetChanged()
seu Adapter
objeto depois de modificar os dados nesse adaptador.
Algumas informações adicionais sobre como / quando ligar notifyDataSetChanged()
podem ser visualizadas neste vídeo de E / S do Google .
Também você pode usar isso:
myListView.invalidateViews();
invalidateViews()
nos conjuntos de códigos-fonte, mDataChanged = true;
então não tenho certeza se isso resulta em um melhor desempenho em comparação comnotifyDataSetChanged()
invalidate()
, invalidateViews()
,requestLayout()
, ... respostas para essa pergunta.A coisa certa a fazer (e felizmente também marcada como resposta certa) é ligar notifyDataSetChanged()
para o seu adaptador .
Se a chamada notifyDataSetChanged()
não funcionar, todos os métodos de layout também não ajudarão. Acredite em mimListView
foi atualizado corretamente. Se você não encontrar a diferença, precisará verificar de onde vêm os dados no seu adaptador.
Se esta é apenas uma coleção que você está mantendo na memória, verifique se você realmente excluiu ou adicionou os itens à coleção antes de chamar o notifyDataSetChanged()
.
Se você estiver trabalhando com um banco de dados ou serviço de back-end, precisará chamar o método para recuperar as informações novamente (ou manipular os dados da memória) antes de chamar o notifyDataSetChanged()
.
A coisa é essa notifyDataSetChanged
só funciona se o conjunto de dados tiver sido alterado. Portanto, esse é o lugar para procurar, se você não encontrar mudanças. Depure se necessário.
Descobri que trabalhar com um adaptador que permite gerenciar a coleção, como um BaseAdapter, funciona melhor. Alguns adaptadores como o ArrayAdapter já gerenciam sua própria coleção, dificultando o acesso à coleção adequada para atualizações. Na verdade, é apenas uma camada extra desnecessária de dificuldade na maioria dos casos.
É verdade que isso precisa ser chamado do thread da interface do usuário. Outras respostas têm exemplos de como conseguir isso. No entanto, isso é necessário apenas se você estiver trabalhando nessas informações fora do segmento da interface do usuário. Isso é de um serviço ou de um segmento que não é da interface do usuário. Em casos simples, você estará atualizando seus dados com um clique no botão ou outra atividade / fragmento. Ainda dentro do segmento da interface do usuário. Não é necessário sempre exibir o runOnUiTrhead.
Pode ser encontrado em https://github.com/hanscappelle/so-2250770.git . Basta clonar e abrir o projeto no Android Studio (gradle). Este projeto tem uma MainAcitivity construindo umListView
com todos os dados aleatórios. Esta lista pode ser atualizada usando o menu de ação.
A implementação do adaptador que criei para este exemplo ModelObject expõe a coleta de dados
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 da 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();
}
}
Outro bom post sobre o poder do listViews é encontrado aqui: http://www.vogella.com/articles/AndroidListView/article.html
adapter.clear()
, e adapter.addAll(Array<T>)
antes de ligarnotifyDataSetChanged()
Ligue para executar quando quiser:
runOnUiThread(run);
OnCreate()
, você define seu thread executável:
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
runOnUiThread()
função
run = new Runnable()
e apenas dei à minha função um public void refreshUIThread()
método que modela o seu run()
método (que também funciona).
Eu tenho alguns problemas com a atualização dinâmica do meu listview.
Ligue para notifyDataSetChanged () no seu adaptador.
Algumas informações adicionais sobre como / quando chamar notifyDataSetChanged () podem ser visualizadas neste vídeo de E / S do Google.
notifyDataSetChanged () não funcionou corretamente no meu caso [chamei o notifyDataSetChanged de outra classe]. Apenas no caso, editei o ListView na atividade em execução (Thread). Esse vídeo, graças a Christopher, deu a dica final.
Na minha segunda aula eu usei
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
para acessar a atualização () da minha Atividade. Esta atualização inclui
myAdapter.notifyDataSetChanged();
para dizer ao adaptador para atualizar a exibição. Funcionou bem, tanto quanto eu posso dizer.
Se você estiver usando o SimpleCursorAdapter, tente chamar requery () no objeto Cursor.
Se você ainda não estiver satisfeito com o Refreshment do ListView, pode ver este trecho, isto é, para carregar o listView do DB. Na verdade, o que você precisa fazer é simplesmente recarregar o ListView, depois de executar qualquer operação CRUD. Não é a melhor maneira de código, mas atualizará o ListView como desejar.
Funciona para Mim .... se você encontrar uma solução melhor, por favor Compartilhe ...
....... ...... faça suas operações CRUD .. ...... ..... DBAdapter.open (); DBAdapter.insert_into_SingleList (); // Traga esse DB_results e adicione-o à lista como seu conteúdo .... ls2.setAdapter (novo ArrayAdapter (DynTABSample.this, android.R.layout.simple_list_item_1, DBAdapter.DB_ListView)); DBAdapter.close ();
As soluções propostas pelas pessoas nesta postagem funcionam ou não, dependendo principalmente da versão Android do seu dispositivo. Por exemplo, para usar o método AddAll, você deve colocar android: minSdkVersion = "10" no seu dispositivo Android.
Para resolver essas questões para todos os dispositivos, criei meu próprio método no meu adaptador e uso dentro do método add and remove herdado do ArrayAdapter que atualiza seus dados sem problemas.
Meu código: usando minha própria classe de dados RaceResult, você usa seu próprio modelo de dados.
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!!!
Se você deseja manter sua posição de rolagem ao atualizar e pode fazer isso:
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 obter informações detalhadas, consulte Android ListView: mantenha sua posição de rolagem ao atualizar .
Basta usar myArrayList.remove(position);
dentro de um ouvinte:
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
myArrayList.remove(position);
myArrayAdapter.notifyDataSetChanged();
}
});
Você precisa usar um único objeto dessa lista em que dados você está inflando ListView
. Se a referência for alterada, notifyDataSetChanged()
não funcionará. Sempre que você estiver excluindo elementos da exibição em lista, exclua-os da lista que está usando, seja um ArrayList <> ou Algo mais, em seguida, Chame o
notifyDataSetChanged()
objeto da classe do seu adaptador.
Então veja aqui como eu consegui no meu adaptador veja abaixo
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();
}
}
Após excluir os dados da exibição em lista, você deve ligar refreshDrawableState()
. Aqui está o exemplo:
final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
db.open();
db.deleteContact(arg3);
mListView.refreshDrawableState();
db.close();
e deleteContact
método em DatabaseHelper
sala de aula será um pouco parecido com
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
Não consegui que o notifyDataSetChanged () funcionasse para atualizar meu SimpleAdapter; então, tentei primeiro remover todas as visualizações que estavam anexadas ao layout pai usando removeAllViews (), depois adicionar o ListView e funcionou, permitindo atualizar o UI:
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 mim, depois de alterar as informações no banco de dados sql, nada poderia atualizar a exibição de lista (para ser uma exibição de lista expansível específica); portanto, se notifyDataSetChanged () não ajudar, tente limpar sua lista primeiro e adicioná-la novamente depois dessa chamada notifyDataSetChanged (). Por exemplo
private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();
Espero que faça sentido para você.
enquanto estiver usando o SimpleCursorAdapter, é possível chamar changeCursor (newCursor) no adaptador.
Eu era o mesmo quando, em um fragmento, queria preencher um ListView (em um único TextView) com o endereço mac de dispositivos BLE digitalizados por algum tempo.
O que eu fiz foi o seguinte:
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();
}
}
Em seguida, o ListView começou a preencher dinamicamente com o endereço mac dos dispositivos encontrados.
Eu acho que depende do que você quer dizer com atualização. Você quer dizer que a exibição da GUI deve ser atualizada ou as exibições filho devem ser atualizadas para que você possa chamar programaticamente getChildAt (int) e obter a exibição correspondente ao que está no adaptador.
Se você deseja que a exibição da GUI seja atualizada, chame notifyDataSetChanged () no adaptador. A GUI será atualizada quando o próximo redesenho.
Se você deseja chamar getChildAt (int) e obter uma visualização que reflita o que está no adaptador, ligue para layoutChildren (). Isso fará com que a exibição filho seja reconstruída a partir dos dados do adaptador.
Eu tinha um ArrayList que queria exibir em uma lista. ArrayList continha elementos do mysql. Eu substituí o método onRefresh e nesse método usei tablelayout.removeAllViews (); e depois repetiu o processo para obter dados novamente do banco de dados. Porém, antes disso, limpe seu ArrayList ou qualquer estrutura de dados, ou então novos dados serão anexados ao antigo.
Se você deseja atualizar a exibição da lista da interface do usuário a partir de um serviço, torne o adaptador estático em sua atividade Principal e faça o seguinte:
@Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
Se você estiver seguindo as diretrizes do Android e estiver usando o ContentProviders
para obter dados Database
e estiver exibindo-o ListView
noCursorLoader
e CursorAdapters
, todas as alterações nos dados relacionados serão refletidas automaticamente no ListView.
Seu getContext().getContentResolver().notifyChange(uri, null);
cursor no ContentProvider
será suficiente para refletir as alterações. Não há necessidade de trabalho extra.
Mas quando você não estiver usando tudo isso, precisará informar ao adaptador quando o conjunto de dados está sendo alterado. Você também precisa preencher novamente / recarregar seu conjunto de dados (por exemplo, lista) e, em seguida, ligar paranotifyDataSetChanged()
para o adaptador.
notifyDataSetChanged()
não funcionará se não houver alterações no conjunto de dados. Aqui está o comentário acima do método nos documentos
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
Eu só consegui obter notifyDataSetChanged apenas obtendo novos dados do adaptador, redefinindo o adaptador para a exibição em lista e fazendo a chamada da seguinte maneira:
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
em outra opção é o onWindowFocusChanged
método, mas com certeza é sensível e precisa de uma codificação extra para quem está interessado
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 você passou uma lista para o seu adaptador.
Usar:
list.getAdapter().notifyDataSetChanged()
para atualizar sua lista.
Se eu falei sobre o meu cenário aqui, as respostas que não estão acima não funcionarão porque eu tinha uma atividade que mostra a lista de valores de banco de dados junto com um botão de exclusão e quando um botão de exclusão é pressionado, eu queria excluir esse item da lista.
O legal é que eu não usei a exibição do reciclador, mas uma exibição de lista simples e essa exibição de lista inicializada na classe do adaptador. Portanto, chamar o notifyDataSetChanged()
não fará nada dentro da classe do adaptador e mesmo na classe de atividade em que o objeto do adaptador é inicializado porque o método delete estava na classe do adaptador.
Portanto, a solução foi remover o objeto do adaptador no getView
método de classe do adaptador (para excluir apenas esse objeto específico, mas se você quiser excluir tudo, chameclear()
).
Para você ter uma ideia, como era meu 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: aqui remove()
e getItem()
métodos são herdados da classe Adapter.
remove()
- para remover o item específico clicadogetItem(position)
- é obter o item (aqui, esse é o meu objeto do Word que eu adicionei à lista) da posição clicada.É assim que eu configuro o adaptador para o listview na classe de atividade,
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);
O mais fácil é fazer um novo Adaper e largar o antigo:
myListView.setAdapter(new MyListAdapter(...));