适配器上的 arraylist.remove() 删除了 ListView 中的错误项目

arraylist.remove() on adapter deletes wrong item in ListView

我有一个名为 AdapterActionsAdapter 扩展 ArrayAdapter<Action>。我给 AdapterActions 一个 ArrayList<Action>,它用那个 ArrayList 填充一个 ListView。问题是这样的:

假设这是我的 ArrayList:

ListaActions(0) = a
ListaActions(1) = b
ListaActions(2) = c
ListaActions(3) = d
ListaActions(4) = e

然后我执行 ListaActions.remove(2) 然后我清除每个 Action 对象并将其重新添加到 ListaActions,它成功地从 ArrayList 中删除了第三个对象,但在 ListView 中它删除最后一项(在本例中为 ListaActions(4) 的视图)并将该 ListView 上表示的已删除对象 (ListaActions(4)) 的每个 属性 设置为其默认值,即:int属性为 0,布尔属性为真...

当我关闭应用程序然后再次打开它时,它仍然是一样的,它不是 "refresh" ListView。有什么想法吗?

我读过一些关于 notifyDataSetChanged() 的帖子,但我认为如果它的作用是 "Notify that the Data Set changed",它就无法工作,如果当我重新启动应用程序时它会重建所有内容和那些"empty" 视图仍然存在,ArrayList 上的最后一个对象未​​在列表视图中表示。

这是AdapterActions的代码:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;

    import android.annotation.SuppressLint;
    import android.app.Activity;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.Switch;
    import android.widget.TextView;
    import android.widget.Toast;


    /*@SuppressLint("ViewHolder") */public class AdapterActions extends ArrayAdapter<Action>{

        // our ViewHolder.
        // caches our TextView
        static class ViewHolderItem {
            TextView codigo;
            TextView accion;
            TextView evento;
            Switch UnSwitch;
            boolean isChecked1;
        }

        Activity context;
        ArrayList<Action> listaActions;
        @SuppressLint("UseSparseArrays") Map <Integer, Boolean> map = new HashMap<Integer, Boolean>();

        // Le pasamos al constructor el contexto y la lista de contactos
        public AdapterActions(Activity context, ArrayList<Action> listaActions) {
            super(context, R.layout.layout_adapter_actions, listaActions);
            this.context = context;
            this.listaActions = listaActions;




        }

        public View getView(final int position, View convertView, ViewGroup parent) {

            ViewHolderItem viewHolder;

            // Rescatamos cada item del listview y lo inflamos con nuestro layout
            //View item = convertView;
            //item = context.getLayoutInflater().inflate(R.layout.layout_adapter_actions, null);
            // well set up the ViewHolder
            viewHolder = new ViewHolderItem();

            if(convertView==null){

                // inflate the layout
                LayoutInflater inflater = ((Activity) context).getLayoutInflater();
                convertView = inflater.inflate(R.layout.layout_adapter_actions, parent, false);

                // well set up the ViewHolder
                viewHolder.codigo = (TextView) convertView.findViewById(R.id.codigo);
                viewHolder.accion = (TextView) convertView.findViewById(R.id.accion);
                viewHolder.evento = (TextView) convertView.findViewById(R.id.evento);
                viewHolder.UnSwitch = (Switch) convertView.findViewById(R.id.activa);
                convertView.setLongClickable(true);

                // store the holder with the view.
                convertView.setTag(viewHolder);

            }else{
                // we've just avoided calling findViewById() on resource everytime
                // just use the viewHolder
                viewHolder = (ViewHolderItem) convertView.getTag();
            }

            Action a = new Action(0, 1, 1, true);
            AccionDefinida LaAccion = new AccionDefinida(0, 0, "", "");
            EventoDefinido ElEvento = new EventoDefinido(0, 0, "", "");

            //ACTIONS
                    ActionsSQLite base = new ActionsSQLite(context, "Actions", null,1);
                    SQLiteDatabase db1 = base.getReadableDatabase();
                    db1 = context.openOrCreateDatabase("Actions",SQLiteDatabase.OPEN_READONLY, null);

                    String query = "SELECT * FROM Actions WHERE Id = " + String.valueOf(position + 1);
                    Cursor c1 = db1.rawQuery(query, null);

                    try{
                        if(c1!=null){

                            int i = c1.getColumnIndexOrThrow("Id");
                            int j = c1.getColumnIndexOrThrow("IdAccionDefinida");
                            int k = c1.getColumnIndexOrThrow("IdEventoDefinido");
                            int l = c1.getColumnIndexOrThrow("Activa");
                            boolean esActiva;

                            //Nos aseguramos de que existe al menos un registro
                            while(c1.moveToNext()){
                                if (c1.getInt(l) == 0){
                                    esActiva = false;
                                } else
                                {
                                    esActiva = true;
                                }
                                //Recorremos el cursor hasta que no haya más registros
                                a = new Action(c1.getInt(i), c1.getInt(j), c1.getInt(k), esActiva);
                            }
                        }
                        else 
                            Toast.makeText(context.getApplicationContext(), 
                                      "No hay nada :(", Toast.LENGTH_LONG).show();
                      }
                      catch (Exception e){
                        Log.i("bdActions", "Error al abrir o crear la base de datos" + e); 
                      }

                      if(db1!=null){
                            db1.close();
                    }   


            //EVENTOS
            EventosDefinidosSQLite base2 = new EventosDefinidosSQLite(this.context, "EventosDefinidos", null, 1);
            SQLiteDatabase db2 = base2.getReadableDatabase();
            db2 = context.openOrCreateDatabase("EventosDefinidos",SQLiteDatabase.OPEN_READONLY, null);

            String query2 = "SELECT * FROM EventosDefinidos WHERE Id = " + a.getIdEventoDefinido();
            Cursor c2 = db2.rawQuery(query2, null);
            try{
                if(c2!=null){

                    int h = c2.getColumnIndexOrThrow("Id");
                    int k = c2.getColumnIndexOrThrow("IdEvento");
                    int i = c2.getColumnIndexOrThrow("Nombre");
                    int j = c2.getColumnIndexOrThrow("Aux");

                    //Nos aseguramos de que existe al menos un registro
                    while(c2.moveToNext()){
                        //Recorremos el cursor hasta que no haya más registros
                        ElEvento = new EventoDefinido(c2.getInt(h), c2.getInt(k), c2.getString(i), c2.getString(j));
                    }
                }
                else 
                    Toast.makeText(context.getApplicationContext(), 
                          "No hay nada :(", Toast.LENGTH_LONG).show();
            }
            catch (Exception e){
                Log.i("bdEventos", "Error al abrir o crear la base de datos" + e); 
            }

            if(db2!=null){
                db2.close();
            }   
            //ACCIONES
            AccionesDefinidasSQLite base3 = new AccionesDefinidasSQLite(context, "AccionesDefinidas", null,1);
            SQLiteDatabase db3 = base3.getReadableDatabase();
            db3 = context.openOrCreateDatabase("AccionesDefinidas", SQLiteDatabase.OPEN_READONLY, null);

            String query3 = "SELECT * FROM AccionesDefinidas WHERE Id = " + String.valueOf(a.getIdAccionDefinida());
            Cursor c3 = db3.rawQuery(query3, null);
            try{
                if(c3!=null){

                    int h = c3.getColumnIndexOrThrow("Id");
                    int k = c3.getColumnIndexOrThrow("IdAccion");
                    int i = c3.getColumnIndexOrThrow("Nombre");
                    int j = c3.getColumnIndexOrThrow("Aux");

                    //Nos aseguramos de que existe al menos un registro
                    while(c3.moveToNext()){
                        //Recorremos el cursor hasta que no haya más registros
                        LaAccion = new AccionDefinida(c3.getInt(h), c3.getInt(k), c3.getString(i), c3.getString(j));
                    }
                }
                else 
                    Toast.makeText(context.getApplicationContext(), 
                          "No hay nada :(", Toast.LENGTH_LONG).show();
            }
            catch (Exception e){
                Log.i("bdAcciones", "Error al abrir o crear la base de datos" + e); 
            }

            if(db3!=null){
                db3.close();
            }

            // object item based on the position


            // assign values if the object is not null
            if(a != null) {
                // get the TextView from the ViewHolder and then set the text (item name) and tag (item ID) values
                viewHolder.codigo.setText(String.valueOf(a.getId()));
                viewHolder.codigo.setTag(a.getId());
                viewHolder.accion.setText(LaAccion.getNombre());
                viewHolder.evento.setText(ElEvento.getNombre());
                viewHolder.UnSwitch.setChecked(a.getActiva());
                Log.e("Position+1: "+ String.valueOf(position+1), "Id: "+ String.valueOf(a.getId()));


            }

            viewHolder.isChecked1 = viewHolder.UnSwitch.isChecked();
            map.put(position, viewHolder.isChecked1);
            viewHolder.UnSwitch.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("OnClick", "Se clickeó");

                    listaActions.get(position).setActiva(map.get(position));
                    Log.e("Listener del switch", "Modificó la action en la lista de la ActividadPrincipal");
                    int isActiva;
                    if(map.get(position) == true)
                    {
                        isActiva = 0;
                        Log.e("Listener del switch", "isActiva = 1");
                        map.put(position, false);
                    } else
                    {
                        isActiva = 1;
                        Log.e("Listener del switch", "isActiva = 0");
                        map.put(position, true);
                    }
                    String query = "UPDATE Actions SET Activa = " + String.valueOf(isActiva) + " WHERE Id = " + String.valueOf(position+1);
                    Log.e("Consulta:", query);
                    Log.e("Listener del switch", "query creado");
                    Log.e("La position debería ser", String.valueOf(position+1));
                    Log.e("Y el valor del map.get(position) es", String.valueOf(map.get(position)));
                    ActionsSQLite helper1  = new ActionsSQLite(context, "Actions", null, 1);
                    Log.e("Listener del switch", "Creo el helper");
                    SQLiteDatabase db = helper1.getWritableDatabase();
                    Log.e("Listener del switch", "obtenida la base escribible");
                    db.execSQL(query);
                    Log.e("Listener del switch", "Query ejecutado");
                }
            });



            return convertView;


        }

当我从 ArrayList 中删除一个对象时,我是从这个对话框中进行的:

import java.util.ArrayList;

import android.app.Activity;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;


public class DialogModificarAction extends DialogFragment
{    

    public interface DialogModificarActionListener { 

        }



    private int getIndex(Spinner spinner, ArrayList<String> MiLista){

        int index = 0;

            for(int j = 0;j<=MiLista.size();j++){
            if (spinner.getSelectedItem().equals(MiLista.get(j))){
                index = j;
                break;
            }
        }
        return index;
}

    private Button cancelButton;
    private Button confirmButton;
    private Button deleteButton;
    private Spinner spinnerAccion;
    private Spinner spinnerEvento;
    private static Action ActionAModificar = null;
    private ArrayList<EventoDefinido> ListaEventosDefinidos = new ArrayList<EventoDefinido>();
    private ArrayList<AccionDefinida> ListaAccionesDefinidas = new ArrayList<AccionDefinida>();
    private ArrayList<String> ListaNombresAccionesDefinidas = new ArrayList<String>();
    private ArrayList<String> ListaNombresEventosDefinidos = new ArrayList<String>();

    public DialogModificarAction()
    {
        // Empty constructor required for DialogFragment
    }    
    @SuppressWarnings("unused")
    private DialogModificarActionListener dialogListener;


    //This is how you can supply your fragment with information
    public static DialogModificarAction newInstance(ArrayList<Action> ListaActions, ArrayList<AccionDefinida> ListaAccionesDefinidas, ArrayList<EventoDefinido> ListaEventosDefinidos, Action ActionAModificar)
    {
        DialogModificarAction myDialog = new DialogModificarAction();
        Bundle args = new Bundle();
        args.putParcelableArrayList("ListaActions", ListaActions);
        args.putParcelableArrayList("ListaAccionesDefinidas", ListaAccionesDefinidas);
        args.putParcelableArrayList("ListaEventosDefinidos", ListaEventosDefinidos);
        args.putParcelable("ActionAModificar", ActionAModificar);
        myDialog.setArguments(args);
        return myDialog;
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0); // Le saca el título al DialogFragment

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialog_modif_baja_action, container);

        //DECLARO LOS ELEMENTOS EN EL LAYOUT
        ActionAModificar = getArguments().getParcelable("ActionAModificar");
        ListaAccionesDefinidas = getArguments().getParcelableArrayList("ListaAccionesDefinidas");
        ListaEventosDefinidos = getArguments().getParcelableArrayList("ListaEventosDefinidos");

        spinnerAccion = (Spinner)view.findViewById(R.id.spinnerAccionDefinida);
        spinnerEvento = (Spinner)view.findViewById(R.id.spinnerEventoDefinido);

        AccionDefinida AccionTemporal;
        for(int i = 0; i<= ListaAccionesDefinidas.size()-1;i++)
        {
            AccionTemporal = ListaAccionesDefinidas.get(i);
            ListaNombresAccionesDefinidas.add(AccionTemporal.getNombre());
        }

        EventoDefinido EventoTemporal;
        for(int i = 0; i<= ListaEventosDefinidos.size()-1;i++)
        {
            EventoTemporal = ListaEventosDefinidos.get(i);
            ListaNombresEventosDefinidos.add(EventoTemporal.getNombre());
        }


        ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this.getActivity(), android.R.layout.simple_spinner_item, ListaNombresAccionesDefinidas);
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerAccion.setAdapter(dataAdapter);
        spinnerAccion.setSelection(ActionAModificar.getIdAccionDefinida()-1);

        ArrayAdapter<String> dataAdapter2 = new ArrayAdapter<String>(this.getActivity(), android.R.layout.simple_spinner_item, ListaNombresEventosDefinidos);
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerEvento.setAdapter(dataAdapter2);
        spinnerEvento.setSelection(ActionAModificar.getIdEventoDefinido()-1);

        //Setup cancel button listener
        cancelButton = (Button) view.findViewById(R.id.cancelButton);
        cancelButton.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //Cerrar diálogo y no hacer nada
                getDialog().dismiss();
            }
        });

        //Setup confirm button listener
        confirmButton = (Button) view.findViewById(R.id.confirmButton);
        confirmButton.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //Obtengo los ids de la accion y el evento
                int IdAccionDefinidaSeleccionada;
                int IdEventoDefinidoSeleccionado;

                IdAccionDefinidaSeleccionada = ListaAccionesDefinidas.get(getIndex(spinnerAccion, ListaNombresAccionesDefinidas)).getId();
                IdEventoDefinidoSeleccionado = ListaEventosDefinidos.get(getIndex(spinnerEvento, ListaNombresEventosDefinidos)).getId();
                ActionAModificar.setIdAccionDefinida(IdAccionDefinidaSeleccionada);
                ActionAModificar.setIdEventoDefinido(IdEventoDefinidoSeleccionado);

                ActividadPrincipal.listaActions.get(ActionAModificar.getId()).setIdAccionDefinida(IdAccionDefinidaSeleccionada);
                ActividadPrincipal.listaActions.get(ActionAModificar.getId()).setIdEventoDefinido(IdEventoDefinidoSeleccionado);

                //Agrego la Action a la base de datos
                ActionsSQLite helper1  = new ActionsSQLite(getActivity(), "Actions", null, 1);
                SQLiteDatabase db = helper1.getWritableDatabase();
                String myQuery = "UPDATE Actions SET IdAccionDefinida = " + String.valueOf(IdAccionDefinidaSeleccionada) + ", IdEventoDefinido = " + String.valueOf(IdEventoDefinidoSeleccionado) + " WHERE Id = " + String.valueOf(ActionAModificar.getId());
                db.execSQL(myQuery);

                ActividadPrincipal parent = (ActividadPrincipal) getActivity();
                parent.listView.invalidateViews();
                //Cerrar diálogo
                getDialog().dismiss();


            }
        });

        deleteButton = (Button) view.findViewById(R.id.deleteButton);
        deleteButton.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                String query = "DELETE FROM Actions WHERE Id = " + String.valueOf(ActionAModificar.getId());
                ActionsSQLite helper1  = new ActionsSQLite(getActivity(), "Actions", null, 1);
                SQLiteDatabase db = helper1.getWritableDatabase();
                db.execSQL(query);
                Log.e("Listener del switch", "Query ejecutado");
                ActividadPrincipal.listaActions.remove(ActionAModificar.getId()-1);
                ActividadPrincipal.adapterActions.notifyDataSetChanged();

                //Cerrar diálogo
                getDialog().dismiss();
            }
        });


        return view;
    }

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        if (activity instanceof DialogModificarActionListener) 
        {
            dialogListener = (DialogModificarActionListener) activity; 
        }
        else
        {
            throw new ClassCastException(activity.toString() + " must implement dialogListener.DialogActionsListener");
        }
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        ActividadPrincipal parent = (ActividadPrincipal) getActivity();
        parent.ActualizarActions();

    }
}

如果更改列表中的数据,则必须调用适配器的 notifyDataSetChanged 方法才能正常运行。

除此之外您可能还有其他错误,但 notifyDataSetChanged 绝对是必需的。先补充一下,如果还有问题再继续排查。

编辑:既然您已经展示了您的代码,我对它不起作用并不感到惊讶。您显然混淆了几个应该是独立实体的概念。特别是,您不应该从适配器访问数据库。

我不愿意花时间去尝试完全理解你的代码,坦率地说,它是一团糟,所以让我描述一下 应该走近了。

有两种基本情况。在第一个场景中,您有一个对象列表,并希望在 ListView 中显示它们。在第二种情况下,您在数据库中有对象,并希望在 ListView 中显示它们。这两种情况可能不应该混合。

在第一个(更简单的)场景中,您只需构造一个 ListAdapter,并将列表提供给适配器。在这种情况下,如果您从列表中删除一个项目,您还必须调用 ListAdapter 的 notifyDataSetChanged() 方法。如果您希望此删除在应用程序的当前 运行 之后持续存在,您可以自行更新列表的原始来源。

在第二种情况下,您将从数据库中获取 Cursor,可能使用从 ContentProvider 获取的 CursorLoader。然后,您将创建一个 CursorAdapter,并将其与 ListView 一起使用。在这种情况下,您要求 ContentProvider 从数据库中删除项目,这将自动导致 ContentProvider 更新 Cursor,并通知您 activity。然后,您将新光标提供给 CursorAdapter,并调用它的 notifyDataSetChanged 方法。

网络上有很多关于如何执行此操作的教程 - 我建议您阅读并查看一些示例。