When I want to delete a specific item from my ListView with custom adapter my last item always gets deleted and if I create a new item dynamically, it still contains the previous Spinner value.

This is my custom item xml


`<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <Spinner
        android:id="@+id/training_spinner"
        android:layout_width="255dp"
        android:layout_height="50dp"
        android:layout_marginEnd="19dp"
        android:layout_toStartOf="@+id/delete_btn" />

    <Button
        android:id="@+id/delete_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginEnd="30dp"
        android:layout_marginRight="30dp"
        android:text="Delete" />


</RelativeLayout>`

This is my activity xml


`<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SelectTrainings">

    <ListView
        android:id="@+id/trainingsListView"
        android:layout_width="410dp"
        android:layout_height="565dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.005">

    </ListView>

    <Button
        android:id="@+id/TrainButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Train!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/trainingsListView" />

    <Button
        android:id="@+id/AddTraining"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Training"
        app:layout_constraintBottom_toTopOf="@+id/TrainButton"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/trainingsListView" />
</androidx.constraintlayout.widget.ConstraintLayout>`

This is the code in the activity


`public class SelectTrainings extends AppCompatActivity
{
    ListView trainingsListView;
    MyCustomAdapter arrayAdapter;
    Button addTraining;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select_trainings);

        trainingsListView = (ListView) findViewById(R.id.trainingsListView);
        ArrayList<Item> list = new ArrayList<Item>();
        arrayAdapter = new MyCustomAdapter(this, list);
        trainingsListView.setAdapter(arrayAdapter);
        addTraining = (Button) findViewById(R.id.AddTraining);
        addTraining.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view) {
                arrayAdapter.putItem();
            }
        });

        trainingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
        {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                list.remove(i);
                arrayAdapter.notifyDataSetChanged();
            }
        });
    }

    public class Item
    {
        private Spinner spinner;
        private Button button;
    }

    public class MyCustomAdapter extends ArrayAdapter
    {
        private ArrayList<Item> items;
        private Context context;

        public MyCustomAdapter(Context context, ArrayList<Item> items) {
            super(context, 0, items);
            this.items = items;
            this.context = context;
            //Handle TextView and display string from your list
        }

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public Object getItem(int pos) {
            return items.get(pos);
        }

        @Override
        public long getItemId(int pos) {
            return 0;
            //just return 0 if your list items do not have an Id variable.
        }

        public void putItem() {
            items.add(new Item());
            arrayAdapter.notifyDataSetChanged();
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            final Item newItem;
            if (convertView == null)
            {
                newItem = new Item();
                convertView = inflater.inflate(R.layout.item_in_view, null);
                newItem.spinner = (Spinner) convertView.findViewById(R.id.training_spinner);

                String[] stringOfPositions = new String[]{
                        "First training",
                        "Second training",
                };

                ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(context,
                        android.R.layout.simple_spinner_item, stringOfPositions);
                spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                newItem.spinner.setAdapter(spinnerAdapter);
                newItem.button = (Button) convertView.findViewById(R.id.delete_btn);

                convertView.setTag(newItem);
            }
            else
            {
                newItem = (Item) convertView.getTag();
            }

            newItem.button.setTag(new Integer(position));
            newItem.spinner.setTag(new Integer(position));
            newItem.button.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v) {
                    Integer tag = (Integer) v.getTag();
                    remove(getItem(tag.intValue()));
                    arrayAdapter.notifyDataSetChanged();
                }
            });



            return convertView;
        }
    }
}`

How can I delete only a specific item from this list/adapter??

I have tried using tags, I have tried using onItemClickListener but nothing worked, I have also tried removing the if (convertView == null) part but that resets values in my spinners every time I add or delete a row.

EDIT: I have also realized that in my code, spinner and button are actually null. Can you help me fix that as well?

New contributor

Jurica Sestok is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

From my observation, the major problem:

  1. MyCustomAdapter.getView() has not passed properly the data to UI components
  2. items, as a Data object shall contain value only and not UI components

In order to resolve the problem:

  • You shall understand how MyCustomAdapter.getView() works
    • e.g.,
    • What the convert view is
    • How the UI component redraw after calling
      notifyDataSetChanged()
    • How do it relate to the items object

At last, here is my suggestion for the Adapter to be worked properly based on your secnario.

You may take a look and hope you get better understanding about it!

Item class

   // CHANGE: It shall store data instead of UI component.
    // UI component shall be catered by adapter and child list view component directly
    public class Item
    {
        int spinnerPosition = 0;

        //private Spinner spinner;
        //private Button button;
    }

MyCustomAdapter class

 public class MyCustomAdapter extends ArrayAdapter {
        private ArrayList<Item> items;
        ...
        ...
        ...
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            Log.d("MyCustomAdapter", "getView: position=" + position +
                    " " + items.get(position) +
                    " " + convertView );



            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            // EXPLAIN:
            // ConvertView == null indicates the current position is init the 1st time
            // Init it and its child view component here (once only for this position)
            // Note that this position will be re-used WHEN the dataset has changed OR screen position renew!
            if (convertView == null) {
                // Inflate parent view for current position
                convertView = inflater.inflate(R.layout.item_in_view, null);

                // Init child component here

                // Spinner in item_in_view
                Spinner spinner = convertView.findViewById(R.id.training_spinner);
                String[] stringOfPositions = new String[]{
                        "First training",
                        "Second training",
                        "Third training",
                        "Forth training"
                };

                // Spinner adapter
                ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(context,
                        android.R.layout.simple_spinner_item, stringOfPositions);
                spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                spinner.setAdapter(spinnerAdapter);

                // Get spinner default position
                int spinnerSelectedPosition = spinner.getSelectedItemPosition();

                // Store the UI component data to [item] var in instance var [this.items]
                Item item = this.items.get(position);
                item.spinnerPosition = spinnerSelectedPosition;

                // Listener to spinner item selected change
                spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                    @Override
                    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
                        item.spinnerPosition = position;
                    }

                    @Override
                    public void onNothingSelected(AdapterView<?> parentView) {
                    }
                });


                // Init delete button
                // Remove the instance var [this.items] position and then call notifyDataSetChanged()
                Button deleteBtn = convertView.findViewById(R.id.delete_btn);
                deleteBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        items.remove(position);
                        //arrayAdapter.notifyDataSetChanged();
                        // Since you are already in ArrayAdapter class, just call notifyDataSetChanged() directly!
                        notifyDataSetChanged();
                    }
                });
            } else {
                // OKAY, here is the main problem
                // I am not sure the list view behaviour but I assume that it is similar to recycler view
                // ContentView will be RE-USED so we will need to put the correct data back to the spinner for current positions

                // Retrieve the data from items
                Item item = this.items.get(position);
                int expectedSpinnerSelectedPosition = item.spinnerPosition;

                // Retrieve the spinner component (As ContentView is not null, it shall be ready to call)
                Spinner spinner = convertView.findViewById(R.id.training_spinner);
                // Update the spinner selection position according to the data retrieved from items
                spinner.setSelection(expectedSpinnerSelectedPosition);
            }

            return convertView;
        }

New contributor

Android Newbie A is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *