如何从列表中(随机地)删除项目并维护 ListView

How to remove items (randomly) from a list and maintaining the ListView

有一个 ListView 显示 Arraylist 的项目,这些项目由用户动态添加(通过 EditText 和确认按钮)。在 Listview 的每个项目中,都有一个 remove ImageView,它从 List 中删除相应的项目(因此从 ListView 中删除)。

目标是使 ListView 上的删除按钮 (ImageView) 正常工作。

目前我正在为每个项目添加一个 onClickListener,当它们被添加到列表中时。 onClickListener 执行一个方法,该方法使用 list.remove(int i) 函数,然后使用 notifyDataSetChanged() 更新 ListView。

[[主要 ACTIVITY]]

public class MainActivity extends AppCompatActivity {

    // setting up member variables
    ArrayList<String> list = new ArrayList<>();
    ArrayAdapter<String> arrayAdapter;
    TextInputLayout textInputLayout;
    ListView listView;
    Button addNewListItem;

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

        initialize();

    }

    private void initialize() {
        // binding listView, arrayadapter & button to the ones from the layout, setting up the array adapter

        // Listview & adapted
        listView = findViewById(R.id.listView_Items);
        arrayAdapter = new ArrayAdapter<>(this, R.layout.listview_item, R.id.listView_item_text, list);
        listView.setAdapter(arrayAdapter);

        // buttons
        addNewListItem = findViewById(R.id.button_addItem);

        // setting up the onclick listener for addItem, de/increasePeople, calculate
        addNewListItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addItem();
            }
        });

        // setting up the onclick listener for the ok button in the soft keyboard for the
        // edit text field when adding new items
        textInputLayout = findViewById(R.id.TextInputLayout_itemToAdd);
        textInputLayout.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    addItem();
                    return true;
                }
                return false;
            }
        });
    }

    private void addItem() {
        textInputLayout = findViewById(R.id.TextInputLayout_itemToAdd);
        String inputItem = textInputLayout.getEditText().getText().toString().trim();

        // checking, if text was entered in the edit text and then adding it to the list
        if (inputItem.isEmpty()) {
            textInputLayout.setError("Can't be empty");
        } else {
            textInputLayout.setError(null);
            list.add(inputItem);

            // Adding an onClickListener to an item of the listview requires the listView to
            // finish redrawing. OnLayoutChangeListener fires as soon as the listview has finished
            // redrawing the listview.
            // The layoutchangelistener is immediately removed and the onclicklistener is added.

            listView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    listView.removeOnLayoutChangeListener(this);

                    // getting the child count of the listview to get the index of the item, that
                    // just has been added
                    int addedItemId = listView.getChildCount();
                    Toast.makeText(MainActivity.this,"addedItemid: " + addedItemId,Toast.LENGTH_SHORT).show();
                    View addedItemChildView = listView.getChildAt(addedItemId-1);

                    // initializing the imageview to which the onclicklistener has to be added
                    ImageView removeImage = addedItemChildView.findViewById(R.id.ImageView_removeItem);

                    // onclicklistener is an anonymous class and therefore requires
                    // the index to be final. initializing a final int with the index
                    final int itemId = addedItemId-1;
                    removeImage.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.d("Logging","itemId: " + itemId);
                            removeListItem(itemId);
                        }
                    });
                }
            });
            arrayAdapter.notifyDataSetChanged();
        }
    }

    private void removeListItem(int listItemId) {
        list.remove(listItemId);
        arrayAdapter.notifyDataSetChanged();
    }
}

[[ACTIVITY_MAIN]]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/linearLayout_mainLayout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <android.support.design.widget.TextInputLayout
        android:id="@+id/TextInputLayout_itemToAdd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorEnabled="true">

        <android.support.design.widget.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_enterItem"
            android:inputType="text" />
    </android.support.design.widget.TextInputLayout>

    <Button
        android:id="@+id/button_addItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button_addItem" />

    <ListView
        android:id="@+id/listView_Items"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


</LinearLayout>

[[LISTVIEW_ITEM]]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginRight="8dp">


    <ImageView
        android:id="@+id/ImageView_removeItem"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="4"
        android:clickable="true"

        app:srcCompat="@android:drawable/ic_menu_delete" />

    <TextView
        android:id="@+id/listView_item_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_weight="2"
        android:gravity="center_vertical"
        tools:text="test" />

    <android.support.design.widget.TextInputLayout
        android:id="@+id/listView_item_edit"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_weight="3"
        android:gravity="center"
        app:errorEnabled="false">

        <android.support.design.widget.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter Value"
            android:inputType="numberDecimal" />
    </android.support.design.widget.TextInputLayout>



</LinearLayout>

如您所见,当将 onClickListener 添加到 ImageView 时,将根据列表中的当前项数(减 1)添加 itemid。 如果我们有 3 个项目:"i0"、"i1" 和 "i2" 和 "i1" 将被删除,"i2" 上带有 remove 方法的 onClickListener 具有旧索引。 "i2"ArrayList 中的索引已自动减少,但 remove-function 参数保持不变。

如何解决这个问题?

谢谢:)

(我希望我没有删除任何必要的代码,因为我试图只留下重要的部分)

可以通过覆盖标准适配器的 getView() 来访问 ListView 项目内的视图。试试这个 [使用 setTag() 和 getTag()]:

    arrayAdapter = new ArrayAdapter(this, R.layout.listview_item, R.id.listView_item_text, list){
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View itemView = super.getView(position, convertView, parent);
            ImageView removeImage = itemView.findViewById(R.id.ImageView_removeItem);

            removeImage.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = (int)v.getTag();
                    Log.d("Logging","itemId: " + pos);
                    remove(pos);
                    notifyDataSetChanged();
                }
            });

            removeImage.setTag(position);
            return itemView;
        }
    };

希望对您有所帮助!

另一个答案[使用final]:

    arrayAdapter = new ArrayAdapter(this, R.layout.listview_item, R.id.listView_item_text, list){
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View itemView = super.getView(position, convertView, parent);
        ImageView removeImage = itemView.findViewById(R.id.ImageView_removeItem);

        removeImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //int pos = (int)v.getTag();
                Log.d("Logging","itemId: " + position);
                remove(position);
                notifyDataSetChanged();
            }
        });

        //removeImage.setTag(position);
        return itemView;
    }
};