Como atualizar o Android ListView?
-
20-09-2019 - |
Pergunta
Como refrescar um Android ListView
Depois de adicionar/excluir dados dinâmicos?
Solução
Ligar notifyDataSetChanged()
Nas suas Adapter
Objeto depois de modificar os dados nesse adaptador.
Alguns detalhes adicionais sobre como/quando ligar notifyDataSetChanged()
pode ser visto em Este vídeo de E/S do Google.
Outras dicas
Também você pode usar isso:
myListView.invalidateViews();
Por favor, ignore todos os invalidate()
, invalidateViews()
, requestLayout()
, ... respostas para esta pergunta.
A coisa certa a fazer (e felizmente também marcada como resposta certa) é chamar notifyDataSetChanged()
no seu adaptador.
Solução de problemas
Se ligar notifyDataSetChanged()
Não funciona todos os métodos de layout também não ajudam. Acredite em mim o ListView
foi atualizado corretamente. Se você não encontrar a diferença, você precisa verificar de onde vem os dados do seu adaptador.
Se esta é apenas uma coleção que você está mantendo na memória que você realmente excluiu ou adicionou o (s) item (s) à coleção antes de ligar para o notifyDataSetChanged()
.
Se você estiver trabalhando com um banco de dados ou back -end de serviço, precisará ligar para o método para recuperar as informações novamente (ou manipular os dados da memória) antes de ligar para o notifyDataSetChanged()
.
O problema é isso notifyDataSetChanged
Funciona apenas se o conjunto de dados tiver alterado. Portanto, esse é o lugar para olhar se você não encontrar alterações chegando. Debug, se necessário.
Arrayadapter vs BaseAdapter
Eu achei que trabalhando 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 a obtenção da coleção adequada para atualizações. É realmente apenas uma camada extra desnecessária de dificuldade na maioria dos casos.
Tópico da interface do usuário
É verdade que isso deve ser chamado do tópico da interface do usuário. Outras respostas têm exemplos sobre como conseguir isso. No entanto, isso só é necessário se você estiver trabalhando nessas informações de fora do tópico da interface do usuário. Isso é de um serviço ou um thread não ui. Em casos simples, você estará atualizando seus dados de um clique de botão ou de outra atividade/fragmento. Então ainda dentro do tópico da interface do usuário. Não há necessidade de sempre colocar esse Runonuitrhead.
Projeto de exemplo rápido
Pode ser encontrado em https://github.com/hanscappelle/so-2250770.git. Basta clonar e abrir o projeto no Android Studio (Gradle). Este projeto tem um edifício de mainacitividade um ListView
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();
}
}
Mais Informações
Outro bom post sobre o poder do ListViews é encontrado aqui: http://www.vogella.com/articles/androidlistview/article.html
Ligue para Runnable sempre que quiser:
runOnUiThread(run);
OnCreate()
, você define seu tópico executável:
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
Eu tive alguns problemas com a atualização dinâmica do meu ListView.
Ligue para o notifyDataSetchanged () no seu adaptador.
Algumas especificidades adicionais sobre como/quando ligar para o notifyDataStEnged () podem ser visualizadas neste vídeo de E/S do Google.
notifyDataSetchanged () não funcionou corretamente no meu caso [Liguei para o notifyDataSetching de outra classe]. No caso, editei o ListView na atividade em execução (thread). Aquele vídeo Obrigado a Christopher deu a dica final.
Na minha segunda aula, 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 visualização. Funcionou bem, tanto quanto posso dizer.
Se você está usando SimpleCursoradapter tente ligar Requery () no objeto cursor.
Se você ainda não está satisfeito com o refresco da ListView, pode olhar para este trecho, isso é para carregar o ListView do banco de dados, 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 ele atualizará o ListView como você deseja ..
Funciona para mim .... se você encontrar uma solução melhor, compartilhe ...
....... ...... 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();
As soluções propostas pelas pessoas nesta postagem funcionam ou não, principalmente, dependendo da versão Android do seu dispositivo. Por exemplo, para usar o método Addall que você deve colocar no Android: minsdkversion = "10" no seu dispositivo Android.
Para resolver essas perguntas para todos os dispositivos, criei meu próprio método no meu adaptador e use dentro do método Adicionar e remover herda do ArrayAdapter que atualiza seus dados sem problemas.
Meu código: usando minha própria classe de dados RacerResult, 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);
}
}
Resultadosgp.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 quando se 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 quando atualizar.
Apenas use 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 que você está inflando ListView
. Se a referência for alterada então notifyDataSetChanged()
Não funciona. Sempre que você estiver excluindo elementos da visualização da lista também exclua -os da lista que você está usando, seja uma Arraylist <> ou outra coisa, então liguenotifyDataSetChanged()
no objeto da sua classe adaptadora.
Então aqui veja como eu consegui no meu adaptador ver 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();
}
}
Depois de excluir dados da visualização da 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
A classe será um pouco
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
Não consegui fazer notificar quedatastechanged () trabalhar para atualizar meu silenciado, então tentei primeiro remover todas as visualizações que foram anexadas ao layout dos pais usando o removeallViews (), depois adicionando o ListView e que funcionou, permitindo -me 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 pode atualizar a visualização da lista (para ser específica visualização de lista expansível); portanto, se notificardatastchanged () não ajudar, você pode tentar limpar sua lista primeiro e adicioná -la novamente após essa chamada notificouDataSetchanged (). 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 o uso do SimpleCursorAdapter pode chamar ChangeCursor (NewCursor) no adaptador.
Eu era o mesmo quando, em um fragmento, queria preencher um ListView (em uma única visão de texto) com o endereço MAC de dispositivos BLE digitalizados ao longo de 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 o endereço MAC dos dispositivos encontrados.
Eu acho que depende do que você quer dizer com atualização. Você quer dizer que a tela da GUI deve ser atualizada ou você quer dizer que as visualizações da criança devem ser atualizadas para que você possa ligar para o GetChildat (int) programaticamente e obter a exibição correspondente ao que está no adaptador.
Se você deseja atualizar o visor da GUI, ligue para o NOTIFYDATASETHANGED () no adaptador. A GUI será atualizada quando o próximo redesenhado.
Se você deseja chamar GetChildat (int) e obter uma visão que reflita o que é o que está no adaptador, chame para layoutChildren (). Isso fará com que a visualização da criança seja reconstruída a partir dos dados do adaptador.
Eu tinha um ArrayList que queria exibir em uma ListView. Arraylist continha elementos do MySQL. Eu superei o método onRefresh e nesse método que usei tablayout.removeallViews (); e depois repetiu o processo para obter dados novamente do banco de dados. Mas antes disso, certifique -se de limpar seu Arraylist ou qualquer que seja o Structre de dados, ou então novos dados serão anexados ao antigo.
Se você deseja atualizar o UI ListView de um serviço, faça o adaptador estático em sua atividade principal e faça isso:
@Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
Se você está passando por linhas -guia do Android e está usando o ContentProviders
Para obter dados de Database
E você está exibindo no ListView
usando o CursorLoader
e CursorAdapters
, então todas as alterações nos dados relacionadas serão automaticamente refletidas no ListView.
Sua getContext().getContentResolver().notifyChange(uri, null);
no cursor no ContentProvider
Será suficiente para refletir as mudanças. Não é necessária para o trabalho extra.
Mas quando você não está usando tudo isso, precisa informar ao adaptador quando o conjunto de dados estiver mudando. Além disso, você precisa re-preencher / recarregar seu conjunto de dados (digamos) e depois precisa ligar notifyDataSetChanged()
no adaptador.
notifyDataSetChanged()
Não funcionará se não houver as alterações no DatSet. Aqui está o comentário acima do método em documentos
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
Só consegui ser notificada no DataSetching, obtendo novos dados adaptadores, depois redefinindo o adaptador para a visualização da lista e, em seguida, fazendo a chamada como assim:
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
em outra opção é onWindowFocusChanged
método, mas com certeza é sensível e precisa de alguma 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 falasse sobre meu cenário aqui, as respostas não acima não funcionarão porque eu tinha atividades que mostram 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 era que eu não usei a visualização do Recycler, mas uma visualização simples da lista e a visualização da lista inicializada na classe adaptadora. Então, chamando o notifyDataSetChanged()
não fará nada dentro da classe adaptadora e mesmo na classe de atividades em que o objeto adaptador é inicializado porque o método de exclusão estava na classe adaptadora.
Portanto, a solução foi remover o objeto do adaptador na classe do adaptador getView
método (para excluir apenas esse objeto específico, mas se você quiser excluir tudo, ligue clear()
).
Para você ter uma ideia, como era o 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()
Os métodos são herdados da classe adaptadora.
remove()
- Para remover o item específico que é clicadogetItem(position)
- é obter o item (aqui, esse é o meu objeto Word que eu adicionei à lista) na posição clicada.
É assim que eu defino 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 é apenas fazer um novo Adaper e soltar o antigo:
myListView.setAdapter(new MyListAdapter(...));