如何将上下文菜单添加到 Table 布局?

How can I add a Context Menu to a Table Layout?

各位程序员大家好

我正在尝试使用一个应用程序来记录我所做的所有费用。 我在表单中输入我的信息,将其添加到 SQLite 数据库并在 Table 布局中的新 activity 中显示它。一切正常,直到那里,或多或少...

这是 Activity 我显示 SQLite 数据的地方 Table

package com.ascendise.ascBudget;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.Configuration;
import android.database.Cursor;
import android.database.SQLException;
import android.icu.text.DecimalFormat;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.CheckBox;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class ShowExpensesActivity extends AppCompatActivity {
    private TableLayout tblExpenses;
    private TextView tvMessage;
    private TableOpenHandler toh = new TableOpenHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Cursor cursor = toh.getCursor(toh.TABLE_NAME_XPNSES);
        Boolean isLandscape = this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_expenses);
        setTitle(R.string.main_button_expenses);

        //region Initialization GUI-Elements
        tblExpenses = findViewById(R.id.tblExpenses);
        tvMessage = findViewById(R.id.tvMessage);
        //endregion

        //region fill Table
        while(cursor.moveToNext()) {
            try {
                TableRow row = new TableRow(this);
                TextView tvArticle = new TextView(this);
                TextView tvCategory = new TextView(this);
                TextView tvDescription = new TextView(this);
                TextView tvDate = new TextView(this);
                CheckBox ckbxNecessity = new CheckBox(this);
                TextView tvPrice = new TextView(this);

                String sArticle = cursor.getString(toh.COLUMN_ARTICLE);
                String sCategory = cursor.getString(toh.COLUMN_CATEGORY);
                String sDescription = cursor.getString(toh.COLUMN_DESC);
                String sDate = cursor.getString(toh.COLUMN_DATE);
                String sPrice = cursor.getString(toh.COLUMN_PRICE);

                DecimalFormat df = new DecimalFormat(getString(R.string.input_decimalFormat));
                sPrice = df.format(Double.parseDouble(sPrice)).replace(',', '\'');

                tvArticle.setText(sArticle);
                tvCategory.setText(sCategory);
                tvDescription.setText(sDescription);
                tvDate.setText(sDate);
                ckbxNecessity.setClickable(false);
                tvPrice.setText(sPrice);

                if (Integer.parseInt(cursor.getString(toh.COLUMN_NT)) == (toh.XPNSES_NECESSITY)) {
                    ckbxNecessity.setChecked(true);
                } else {
                    ckbxNecessity.setChecked(false);
                }

                row.addView(tvArticle);
                row.addView(tvCategory);
                row.addView(tvDescription);
                row.addView(tvDate);
                row.addView(ckbxNecessity);
                row.addView(tvPrice);
                row.setClickable(true);
                registerForContextMenu(row);
                tblExpenses.addView(row);
            }catch(SQLException ex) {
                ex.printStackTrace();
            }catch(Exception ex) {
                ex.printStackTrace();
            }
        }
        //endregion

        //region change TableLayout @ OrientationChange
        if(isLandscape){
            tvMessage.setVisibility(View.INVISIBLE);
            tvMessage.setHeight(0);
            tblExpenses.setColumnCollapsed(2,false);
            tblExpenses.setColumnCollapsed(4, false);
        }else{
            tvMessage.setVisibility(View.VISIBLE);
            tblExpenses.setColumnCollapsed(2,true);
            tblExpenses.setColumnCollapsed(4, true);
        }
        //endregion
    }

    //closes TableHandler when Activity/App gets closed
    @Override
    public void onDestroy(){
        super.onDestroy();
        toh.close();
    }

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

    @Override
    public boolean onContextItemSelected(MenuItem item){
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        switch(item.getItemId()){
            case R.id.delete:
                toh.delete(info.id);
                break;
            case R.id.edit:
                break;
            default:
                break;
        }
        return true;
    }
}

现在我正尝试向该 Table布局添加上下文菜单,这样我就可以删除行,但它并没有真正发挥作用。当我尝试调用 info.id 时出现 NullPointerException。我认为这与我如何为 ContextMenu 注册视图有关。我尝试注册行、TableLayout 等。是否可以这样做,或者它只适用于 ListViews?

这是 StackTrace:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.ascendise.ascBudget, PID: 26615
    java.lang.NullPointerException: Attempt to read from field 'long android.widget.AdapterView$AdapterContextMenuInfo.id' on a null object reference
        at com.ascendise.ascBudget.ShowExpensesActivity.onContextItemSelected(ShowExpensesActivity.java:133)
        at android.app.Activity.onMenuItemSelected(Activity.java:3676)
        at androidx.fragment.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:436)
        at androidx.appcompat.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:196)
        at androidx.appcompat.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:109)
        at com.android.internal.policy.PhoneWindow$PhoneWindowMenuCallback.onMenuItemSelected(PhoneWindow.java:3851)
        at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:761)
        at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:178)
        at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:908)
        at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:898)
        at com.android.internal.view.menu.MenuDialogHelper.onClick(MenuDialogHelper.java:166)
        at com.android.internal.app.AlertController$AlertParams.onItemClick(AlertController.java:1146)
        at android.widget.AdapterView.performItemClick(AdapterView.java:321)
        at android.widget.AbsListView.performItemClick(AbsListView.java:1206)
        at android.widget.AbsListView$PerformClick.run(AbsListView.java:3197)
        at android.widget.AbsListView.run(AbsListView.java:4145)
        at android.os.Handler.handleCallback(Handler.java:809)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7555)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)

我建议看一下 answer posted here 中最重要的部分 来自它:

row.setOnLongClickListener(new OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        String message = "Here is your message"
        setIntent(getIntent().putExtra("message_key", message));
        registerForContextMenu(v);
        openContextMenu(v);
        unregisterForContextMenu(v);
        return true;
    }
});