3
respostas

Exceção em ListView ao Remover Aluno

Peço perdão se há outro tópico sobre isso, eu não consegui localizar nada parecido. Estou na atividade de remover um aluno da agenda. O processor de remover do array está funcionado, mas quando a excução volta para a ListView, tenho recebido essa exceção:

    Process: eti.policarto.agenda, PID: 11014
    java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131230782, class android.widget.ListView) with Adapter(class android.widget.ArrayAdapter)]
        at android.widget.ListView.originalLayoutChildren(ListView.java:1752)
        at android.widget.ListView.layoutChildren(ListView.java:1666)
        at android.widget.AbsListView$CheckForTap.run(AbsListView.java:3518)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7561)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)

Pesquisando sobre, percebi que o erro acontece pois a thread de UI está desincronizada com a thread que fez a ação de exclusão. Tentei resolver vendo alguns posts, mas não consegui.

Gostaria também de enteder a causa raíz disso, se eu errei algo no código ou se API do android mudou em relação a este procedimento.

Segue o código de minha classe:

package eti.policarto.agenda.ui.activity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.List;

import eti.policarto.agenda.R;
import eti.policarto.agenda.dao.AlunoDAO;
import eti.policarto.agenda.model.Aluno;

import static eti.policarto.agenda.ui.activity.ConstantesActivities.CHAVE_ALUNO;

public class ListaAlunosActivity extends AppCompatActivity {

    public static final String TITULO_APPBAR = "Lista de Alunos";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista_alunos);
        setTitle(ListaAlunosActivity.TITULO_APPBAR);

        configurarFabNovoAluno();
    }

    private void configurarFabNovoAluno() {
        final FloatingActionButton botaoMais = findViewById(R.id.active_lista_alunos_fab_novo_aluno);
        botaoMais.setOnClickListener(view -> startActivity(new Intent(ListaAlunosActivity.this, FormularioAlunoActivity.class)));
    }

    @Override
    protected void onResume() {
        super.onResume();
        configurarLista();
    }

    private void configurarLista() {
        ListView listaAlunos = findViewById(R.id.active_lista_de_alunos_list_view);
        configurarListaAdapterView(listaAlunos, new AlunoDAO().todos());
        configurarListaClickListener(listaAlunos);
        configurarListaDuploListener(listaAlunos);
    }

    private void configurarListaClickListener(ListView listaAlunos) {
        listaAlunos.setOnItemClickListener((adapterView, view, position, id) -> {
            Aluno alunoEscolhido = (Aluno) adapterView.getItemAtPosition(position);
            abrirFormularioAluno(alunoEscolhido);
        });
    }

    private void configurarListaDuploListener(ListView listaAlunos) {
        listaAlunos.setOnItemLongClickListener((adapterView, view, position, id) -> {
            Aluno alunoEscolhido = (Aluno) adapterView.getItemAtPosition(position);
            new AlunoDAO().deleteById(alunoEscolhido.getId());
            return true;//informa que vamos consumir o listener... evitando que ele acione outro listener
        });
    }

    private void abrirFormularioAluno(Aluno aluno) {
        Intent intent = new Intent(ListaAlunosActivity.this, FormularioAlunoActivity.class);
        intent.putExtra(CHAVE_ALUNO, aluno);
        startActivity(intent);
    }

    private void configurarListaAdapterView(ListView list, List<Aluno> alunos) {
        ArrayAdapter<Aluno> alunoArrayAdapter = new ArrayAdapter<>(
                this,
                android.R.layout.simple_list_item_1,
                alunos);
        list.setAdapter(alunoArrayAdapter);
    }
}

Obrigado.

3 respostas

Pode nos mostrar a implementação do seu AlunoDao ?

Claro, segue:

package eti.policarto.agenda.dao;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import eti.policarto.agenda.model.Aluno;

public class AlunoDAO {

    private final static List<Aluno> alunos = new ArrayList<>();
    private static Long contIds = 0L;

    static {

        AlunoDAO alunoDAO = new AlunoDAO();

        Aluno aluno = new Aluno();
        aluno.setNome("Guilherme");
        alunoDAO.salvar(aluno);

        aluno = new Aluno();
        aluno.setNome("Amanda");
        alunoDAO.salvar(aluno);

    }

    public void salvar(Aluno aluno) {
        if(aluno.getId() != null){
            editar(aluno);
            return;
        }

        aluno.setId(contIds);
        AlunoDAO.alunos.add(aluno);
        AlunoDAO.contIds++;
    }

    private Aluno findById(Long id) {
        for (Aluno outro : AlunoDAO.alunos) {
            if(id.equals(outro.getId())){
                return outro;
            }
        }
        return null;
    }

    public void editar(Aluno aluno){
        Aluno alunoLocalizado = findById(aluno.getId());
        if(alunoLocalizado != null){
            alunoLocalizado.setNome(aluno.getNome());
            alunoLocalizado.setEmail(aluno.getEmail());
            alunoLocalizado.setTelefone(aluno.getTelefone());
        }
    }

    public void deleteById(Long id) {
        Aluno aluno = findById(id);
        AlunoDAO.alunos.remove(aluno);
    }

    public List<Aluno> todos() {
        return Collections.unmodifiableList(AlunoDAO.alunos);
    }
}

nesse metodo aqui:

 private void configurarListaDuploListener(ListView listaAlunos) {
        listaAlunos.setOnItemLongClickListener((adapterView, view, position, id) -> {
            Aluno alunoEscolhido = (Aluno) adapterView.getItemAtPosition(position);
            new AlunoDAO().deleteById(alunoEscolhido.getId());
            return true;//informa que vamos consumir o listener... evitando que ele acione outro listener
        });
    }

Voce deveria avisar a lista que ela foi alterada:

 private void configurarListaDuploListener(ListView listaAlunos) {
        listaAlunos.setOnItemLongClickListener((adapterView, view, position, id) -> {
            Aluno alunoEscolhido = (Aluno) adapterView.getItemAtPosition(position);
            new AlunoDAO().deleteById(alunoEscolhido.getId());
            listaAlunos.notifyDataSetChanged(); // serve para avisar que teve mudança na lista
            return true;//informa que vamos consumir o listener... evitando que ele acione outro listener
        });
    }