Search code examples
javaandroidnulltablerow

Why am I getting null on a method to delete an Android TableRow?


Problem: I have two TableLayouts that have delete row buttons in each TableRow. When the deleteTableRows() method is called, it has no issue deleting rows for the FILES table, but it fails with a null object error on the AUTHOR table. I've been debugging for hours and can't identify the null causing the issue? I was hoping someone may see what I can't.

What I have done: I've had this working for months but I felt it necessary to split a setupDeleteRowButton method into two separate methods because I needed independent control of layoutParam adjustments for each TableLayout. I figured I could refactor later. Otherwise, the two methods are virutally identical.

I have a deleteTableRow() method that deletes rows from TableLayouts called by the OnClickListener. The two methods that created the TableRows dynamically for the TableLayouts add a tag based identifier for each row created, so when the deleteTableRows() method is called, the correct row is deleted based upon the tag identifier.

What is happening: I'm not seeing any nulls debugging? When I delete a File table row, using the tag as you see in the deleteTableRows() method below - no issue! When I do with the Author table, I get a 'java.lang.String java.lang.Object.toString()' on a null object reference -- but I'm not seeing it? Confused? I've check for null on the table, tblRow and tag variables and nothing is null.

**I've included the working "File" version to compare to the "Author" version of code. I'm still learning Android, so my methods may be a bit clumsy. The TableLayouts are created at the XML activity and all rows added dynamically... including headers.

The delete method it fails here on the if statement this method is called by the setup[table]DeleteRowButton() methods

public static void deleteTableRows(TableLayout table, String tag){
    for (int i=1; i < table.getChildCount(); i++){
        TableRow tblRow = (TableRow) table.getChildAt(i);
        if(tblRow.getTag().toString().equals(tag)) {
            table.removeView(tblRow);
            break;
        }
    }
}

I'm seeing and receiving these on the if statement above This is row 187 in the error below and row 167 in the method call by the OnClickListener in the code below

variables

error

The two setup[table]DeleteRowButton methods (the OnClickListeners are setup the same, passing a unique tag) - called by the setup[table]TableRow() methods

I'm not sure I have an issue with either of these but the "File" version's call to the deleteTableRows() has not issue, the call from the "Author" version apparently does? They both pass String tags, and so the .toString() is used.

public static Button setupFileDeleteRowButton(Context context, TableLayout table, String tag){
    Button btnDelete = new Button(context);
    TableRow.LayoutParams trLayoutParams = new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT);
    trLayoutParams.setMargins(3,3,3,3);
    trLayoutParams.weight = 1;
    trLayoutParams.height =75;
    btnDelete.setLayoutParams(trLayoutParams);
    btnDelete.setBackgroundColor(Color.WHITE);
    btnDelete.setText("-");
    btnDelete.setTypeface(Typeface.DEFAULT,Typeface.BOLD);
    btnDelete.setTextSize(14);
    btnDelete.setGravity(Gravity.CENTER);
    btnDelete.setPadding(5,5,5,5);
    btnDelete.setTag(tag);
    btnDelete.setOnClickListener(v -> {
        String btnTag = btnDelete.getTag().toString();
        deleteTableRows(table, btnTag);
    });
    return btnDelete;
}


public static Button setupAuthorDeleteRowButton(Context context, TableLayout table, String tag){
    Button btnDelete = new Button(context);
    TableRow.LayoutParams trLayoutParams = new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT);
    trLayoutParams.setMargins(3,3,3,3);
    trLayoutParams.weight = 0.05f;
    trLayoutParams.height =75;
    btnDelete.setLayoutParams(trLayoutParams);
    btnDelete.setBackgroundColor(Color.WHITE);
    btnDelete.setText("-");
    btnDelete.setTypeface(Typeface.DEFAULT,Typeface.BOLD);
    btnDelete.setTextSize(14);
    btnDelete.setGravity(Gravity.CENTER);
    btnDelete.setPadding(5,5,5,5);
    btnDelete.setTag(tag);
    btnDelete.setOnClickListener(v -> {
        String btnTag = btnDelete.getTag().toString();
        deleteTableRows(table, btnTag);
    });
    return btnDelete;
}

The two methods that create and pass the table and the specific tags info (Again, no issues with the "Files", just "Author"

            row.addView(setupFileDeleteRowButton(context, table, fileName));
            for(int r=1; r < 2; r++){
                row.setTag(fileName);
                row.addView(addRowTextViewToTable(context, fileName, false));
                row.setClickable(true);
            }


            row.addView(setupAuthorDeleteRowButton(context, table, DateTimestampManager.currentTimestamp()));
            for(int r=1; r<5;r++) {
                row.addView(addEditTextToTable(context, ""));
                row.setClickable(true);
                Log.e("BuildTableLayout", "addEditTextToTable");
            }

Solution

  • I found my fix. I'm still at a loss as to why the previous setup worked with "FILE" version but not the "AUTHOR", but this fix works for both. That being said, it does make sense to look at the getChildAt(0) position since that is always the button position and receives the setTag. Anyhow, here is the fix. I had to include the .getChildAt(0) on the tblRow then get the tag and check.

    public static void deleteTableRows(TableLayout table, String tag){
        for (int i=1; i < table.getChildCount(); i++){
            TableRow tblRow = (TableRow) table.getChildAt(i);
            if(tblRow.getChildAt(0).getTag().toString().equals(tag)) {
                table.removeView(tblRow);
                break;
            }
        }
    }