上下文菜单无法从 ListView 行中删除文件项

Context Menu Fails To Remove A File Item From A ListView Row

我的上下文菜单无法从 ListView 行中删除文件项。 我的代码运行并且 Toast 指示方法正在运行。 没有任何 logcat 错误。我的 ListView 工作正常。

我的问题是:我的代码有什么问题? 请提供正确的代码来解决这个问题。

谢谢。

Java代码:classTestActivity

    public class TestActivity extends ListActivity {

                    private String root;
                    private List<String> item = null;
                    private ListView listview;

                    @Override
                    public void onCreate(Bundle savedInstanceState) {
                        super.onCreate(savedInstanceState);

                        setContentView(R.layout.activity_test);

                        registerForContextMenu(getListView());

                        root = Environment.getExternalStorageDirectory() + File.separator + "/ListTestFiles";
                        getDir(root);

// 01-Nov-2017 For reference added 6 lines of my existing Java code.
listview = getListView();
listview.setOnItemClickListener(new OnItemClickListener() {
                @Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
File file = new File(path.get(position));
String fullPath = new String(path.get(position));

                        } // End of onCreate code.


                    // 02-Nov-2017 For reference added 28 lines of my existing Java code.
                    private void getDir(String dirPath)
                    {
                        currentPath = dirPath;
                        item = new ArrayList<String>();
                        path = new ArrayList<String>();

                        File f = new File(dirPath);
                        File[] files = f.listFiles();

                        if(!dirPath.equals(root))
                        {
                            item.add(root);
                            path.add(root);
                            item.add("../");
                            path.add(f.getParent());
                        }

                        for(i2=0; i2 < files.length; i2++)
                        {
                            File file = files[i2];

                            if(!file.isHidden() && file.canRead()){
                                path.add(file.getPath());
                                if(file.isDirectory()) {
                                    item.add(file.getName() + "/");
                           }else {
                                    item.add(file.getName());
                                }
                            }
                        }

                        ArrayAdapter<String> fileList = new ArrayAdapter<String>(this, R.layout.layout_item_view, R.id.rowtext, item);
                        setListAdapter(fileList);

                    } // End of getDir method


                     @Override
                     public void onCreateContextMenu(ContextMenu menu, View v,
                                                     ContextMenu.ContextMenuInfo menuInfo) {
                         super.onCreateContextMenu(menu, v, menuInfo);
                             MenuInflater inflater = getMenuInflater();
                             inflater.inflate(R.menu.context_menu, menu);
                     }

                    @Override
                    public boolean onContextItemSelected(MenuItem item) {

                    final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();

                        switch (item.getItemId()) {
                            case R.id.context_menu_rename:
                                Toast.makeText(this, "Rename", Toast.LENGTH_SHORT).show();
                                return true;

                            case R.id.context_menu_delete:

                                // TEST #1 the line of code below runs but will Not delete the selected listview row item.
                                ArrayAdapter<String> adapter = new ArrayAdapter<String>(TouchActivity.this, R.layout.layout_item_view, R.id.rowtext);

                                // TEST #2 the line of code below runs but will Not delete the selected listview row item.
                                // ArrayAdapter<String> adapter = new ArrayAdapter<String>(TouchActivity.this, R.layout.layout_item_view);

// 01-Nov-2017 Added 2 New lines of code.
listview.setAdapter(adapter);
adapter.remove(String.valueOf(info.position));

// 01-Nov-2017 Commented out 2 lines of code.
// String index = valueOf(info.position);
// adapter.remove(index);

                                adapter.notifyDataSetChanged();

                                Toast.makeText(getApplicationContext(), "position = " + info.position, Toast.LENGTH_SHORT).show();

                                Toast.makeText(this, "Delete", Toast.LENGTH_SHORT).show();

                                return true;
                            default:
                                return super.onContextItemSelected(item);
                        }
                    }
                }

XML代码:layout_item_view.xml

            <?xml version="1.0" encoding="utf-8"?>
            <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:orientation="horizontal"
            android:background="#231f20">

            <TextView xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/rowtext"
                android:layout_width="fill_parent"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_height="60sp"
                android:textSize="18sp"
                android:layout_marginTop="10dp"/>

            </RelativeLayout>

XML代码:row.xml

        <?xml version="1.0" encoding="utf-8"?>

        <ListView
            android:id="@+id/rowtext"
            android:layout_width="fill_parent"
            android:layout_height="60sp"
            android:textSize="18sp"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="1dp"
            xmlns:android="http://schemas.android.com/apk/res/android" />

XML代码:activity_test.xml

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:longClickable="true"
            android:clickable="true"
            android:orientation="vertical"
            android:layout_alignParentBottom="true"
            android:background="#231f20">
                <FrameLayout
                    android:layout_width="fill_parent"
                    android:layout_above="@+id/header"
                    android:layout_height="wrap_content" >

                    <ListView
                        android:id="@android:id/list"
                        android:layout_width="fill_parent" android:layout_height="wrap_content"
                        android:layout_weight="1" xmlns:android="http://schemas.android.com/apk/res/android"
                        android:fillViewport="true" />
                </FrameLayout>
        </LinearLayout>

Logcat 输出:

// Test 3 I run the app with this code.
case R.id.context_menu_delete:
                fileList.remove(fileList.getItem(info.position));
                fileList.notifyDataSetChanged();
                return true;

// Test 4 I run the app with String.valueOf code because the adapter is an ArrayAdapter<String>
case R.id.context_menu_delete:
                fileList.remove(String.valueOf(fileList.getItem(info.position)));
                fileList.notifyDataSetChanged();
                return true;

// Logcat output is the same for both Test 3 and 4, the app crashes with the error below.

    --------- beginning of crash
11-03 17:40:02.840  18362-18362/com.testing.listapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.testing.listapp, PID: 18362
    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.widget.ArrayAdapter.getItem(int)' on a null object reference
            at com.testing.listapp.TestActivity.onContextItemSelected(TestActivity.java:5413)
            at android.app.Activity.onMenuItemSelected(Activity.java:2905)
            at com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback.onMenuItemSelected(PhoneWindow.java:4701)
            at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:761)
            at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
            at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:904)
            at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:894)
            at com.android.internal.view.menu.MenuDialogHelper.onClick(MenuDialogHelper.java:167)
            at com.android.internal.app.AlertController$AlertParams.onItemClick(AlertController.java:1082)
            at android.widget.AdapterView.performItemClick(AdapterView.java:305)
            at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
            at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053)
            at android.widget.AbsListView.run(AbsListView.java:3860)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Now when I do a long press on a Row item of the ListView, then press Delete on the context menu, all ListView Rows are removed from the view.

让我们看一下onContextItemSelected()中的一些行:

下面这行给你一个全新的ArrayAdapter。它的 getCount() 将 return 0 因为你没有将任何数据列表传递给它:

`ArrayAdapter<String> adapter = new ArrayAdapter<String>(TouchActivity.this, R.layout.layout_item_view, R.id.rowtext);`

现在您将此 "empty" 适配器设置为 ListView 适配器:

listview.setAdapter(adapter);

旧适配器将被丢弃。当前的数据列表为空。所以现在 ListView 是空的。

因为当前的适配器是"empty",所以下面这行将没有效果:

adapter.remove(String.valueOf(info.position));

I restart the activity and the ListView is shown with all rows still present. Nothing was permanently removed.

那是因为你用原来的适配器重新初始化了ListView

编辑

如何删除选中的项目?

您需要做的第一件事是使原始 ListView 适配器成为托管 ListViewActivityFragment 的字段:

private ArrayAdapter<String> fileList;

然后在初始化数据列表后将其实例化并设置为 ListView 的适配器,例如在 getDir() 结束时:

//...
fileList = new ArrayAdapter<String>(this, R.layout.layout_item_view, R.id.rowtext, item);
setListAdapter(fileList);

数据将像以前一样显示。现在您可以在整个 Activity 中访问此适配器,例如 onContextItemSelected():

@Override
public boolean onContextItemSelected(MenuItem item) {
    final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();

    switch (item.getItemId()) {
        case R.id.context_menu_rename:
            Toast.makeText(this, "Rename", Toast.LENGTH_SHORT).show();
            return true;
        case R.id.context_menu_delete:
            fileList.remove(fileList.getItem(info.position));
            fileList.notifyDataSetChanged();
            return true;
        default:
            return super.onContextItemSelected(item);
     }
}

请注意 fileList.getItem(info.position) 必须是唯一值,因为在引擎盖下 ArrayAdapter#remove() 表示 List#remove(),因此第一个匹配的 List 条目将被删除,不要管它指数。如果你想绝对确定你删除了正确的条目,那么你必须使你的数据列表成为一个字段,并通过调用 item.remove(info.position)

删除索引 info.position 处的条目

以及如何使更改永久化?

我们现在实现的是用户可以从数据列表中删除一个条目,ListView 将通过不再显示删除的条目来反映这一点。如果您离开应用程序 ListView/ 适配器将保留它们的状态,并在一段时间后 return 进入它,例如通过按 "HOME" 按钮。

但如果您倾斜设备并导致方向改变(从纵向到横向,反之亦然),则 onCreate() 将被执行并且删除的数据将恢复。如果用户按下 "BACK" 然后点击启动器图标,情况也是如此。

因此,如果您想永久更改,则必须以某种方式持久化它们。 (但我认为这超出了这个问题的范围。)例如参见 [​​=40=]

上的指南