Pergunta

Como refrescar um Android ListView Depois de adicionar/excluir dados dinâmicos?

Foi útil?

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 é clicado
  • getItem(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(...));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top