Search code examples
javaandroidsqliteandroid-sqlite

How can I delete and edit a specific (sqlite db) row from an attached Linear Layout (Android)


I am making an app in Android Studio and I am having some troubles with some methods, sorry if the code is bad, I am a CS student and making something like this for the first time.

  1. Delete method with error
    I want to make a method to delete a chosen row and update the view of the database table. The method I have, is only deleting it, but with errors. By errors I mean: whenever I click the row I want to delete and then click on the delete button, the app is restarts. And it actually deletes the row from the db.
  2. Edit (can't find a way to do it)
    This edit method should be, something like the INSERT method I already have, fully working I think, but it get's the data from one specific chosen row, and open the activity so I can edit whatever I want;

Here is some code for the methods I already have

//this is my onCreate method, if you need it
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        oAPABD = new AjudanteParaAbrirBD(this);
        oSQLiteDB = oAPABD.getWritableDatabase();
        LinearLayout oLL = (LinearLayout)findViewById(R.id.llsv);

        Cursor oCursor = oSQLiteDB.query(
                oAPABD.TABLE_NAME,
                new String[]{"*"},
                null,
                null,
                null,
                null,
                null,
                null);

        boolean bCarryOn = oCursor.moveToFirst();

        while (bCarryOn){
            LinearLayout oLL1 = (LinearLayout)getLayoutInflater().inflate(R.layout.lines1,null);
            oLL1.setId(oCursor.getInt(0)*10+8);

            TextView oED1 = (TextView) oLL1.findViewById(R.id.ED1);
            TextView oED2 = (TextView) oLL1.findViewById(R.id.ED2);
            TextView oED3 = (TextView) oLL1.findViewById(R.id.ED3);

            oED1.setId(oCursor.getInt(0)*10+2);
            oED1.setText(oCursor.getInt(0)+"");

            //COL 2 = ITENS
            //DESCRIPTION OF COL2 ITEM LIST MISSING

            oED2.setId(oCursor.getInt(0)*10+3);
            oED2.setText(oCursor.getInt(2)+"");
            oED3.setId(oCursor.getInt(0)*10+4); //repetido 10+4;
            oED3.setText(oCursor.getString(3));

            oLL.addView(oLL1);
            bCarryOn = oCursor.moveToNext();
        }


    }
//INSERT NEW method
    public void novoPedido(View v){
        Intent intento1 = new Intent(Main2Activity.this, activityPedido.class );
        intento1.putExtra("acao","novoPedido");
        intento1.putExtra("count",(getLastID()+1)+"");
        intento1.putExtra("currentTime",currentTime);

        startActivityForResult(intento1, iRequest_Code1);
    }

//EDIT method
    public void editPedido(View v){
        Intent intento2 = new Intent(Main2Activity.this, activityPedido.class );
        intento2.putExtra("acao","editarPedido");
        
//not implemented

        startActivityForResult(intento2,iRequest_Code2);
    }

//DELETE method
    public void deletePedido(View v){

        //oSQLiteDB.delete(oAPABD.TABLE_NAME, "id = ?" + choosenRowID/10,null);
        oSQLiteDB.execSQL("DELETE FROM " + oAPABD.TABLE_NAME + " WHERE " + oAPABD.COL1 + "=\"" + choosenRowID/10 + "\"");
        oSQLiteDB.close();

        LinearLayout oLL1 = (LinearLayout)findViewById(choosenRowID+8);
        ((LinearLayout) oLL1.getParent()).removeView(oLL1);
    }

//decided to go with activityResult like this (only inser method included)
    protected void onActivityResult(int iReqCode, int iResultCode, Intent iResult){
        oAPABD = new AjudanteParaAbrirBD(this);
        oSQLiteDB = oAPABD.getWritableDatabase();

        if( (iReqCode == iRequest_Code1) && (iResultCode == RESULT_OK)){    //ADICIONAR
            String id = iResult.getStringExtra("id");
            String listaItens = iResult.getStringExtra("itens");
            String mesa = iResult.getStringExtra("mesa");
            String cliente = iResult.getStringExtra("cliente");

            LastID = Integer.parseInt(id);

            ContentValues oCV = new ContentValues();
            oCV.put(oAPABD.COL1, id );
            //oCV.put(oAPABD.COL2, listaItens);
            oCV.put(oAPABD.COL3, mesa);
            oCV.put(oAPABD.COL4, cliente);

            oSQLiteDB.insert(oAPABD.TABLE_NAME,null,oCV);

            //Toast.makeText(this, "Pedido adicionado com sucesso!", Toast.LENGTH_SHORT).show();
        }else if((iReqCode == iRequest_Code2) && (iResultCode == RESULT_OK)){ //EDITAR

        }else if((iReqCode == iRequest_Code3) && (iResultCode == RESULT_OK)){ //REMOVER
            //Toast.makeText(this, "Pedido removido com sucesso!", Toast.LENGTH_SHORT).show();
        }else{
            //Toast.makeText(this, "Ups! Algo correu mal...", Toast.LENGTH_SHORT).show();
        }
    }

on my other activity, the one which I'm using for inserting and then it gives me the result back is defined like this:

public class activityPedido extends Activity {
    AjudanteParaAbrirBD oAPABD;
    SQLiteDatabase oSQLiteDB;
    TextView tvAction;
    EditText etInfo1,etInfo2,etInfo3,etCliente,etEmpregado,etPedido;
    ImageButton bSaveNew,bSaveEdit;

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

        Intent iCameFromAct2 = getIntent();
        String action = iCameFromAct2.getStringExtra("acao");
        String currentTime = iCameFromAct2.getStringExtra("currentTime");
        tvAction = (TextView)findViewById(R.id.tAction);
        etInfo1 = (EditText)findViewById(R.id.etInfo1);
        bSaveNew = (ImageButton)findViewById(R.id.bSaveNew);
        bSaveEdit = (ImageButton)findViewById(R.id.bSaveEdit);
        etInfo3 = (EditText)findViewById(R.id.etInfo3);
        etPedido = (EditText)findViewById(R.id.etPedido);

        if (action.equals("novoPedido")){
            tvAction.setText("Novo Pedido");
            etInfo3.setText(currentTime);
            bSaveEdit.setVisibility(View.GONE);

            etInfo1.setText(iCameFromAct2.getStringExtra("count"));
            etInfo1.setInputType(InputType.TYPE_NULL);
        }
        if (action.equals("editarPedido")){
            tvAction.setText("Editar Pedido");
            bSaveNew.setVisibility(View.GONE);

        }
        if (action.equals("removerPedido")){
            tvAction.setText("Remover Pedido");
        }

    }//onCreate

/*Using this to make the "same button" with 2 different onClick methods:
saveNew(save data when clicked ADD NEW in the previous activity)
and saveEdit(bellow)
which I haven't yet done anything to it and is the one I really needed help,
because I can't figure out out to make it)*/
    public void saveNew(View v){
        etInfo1 = (EditText)findViewById(R.id.etInfo1);
        etInfo2 = (EditText)findViewById(R.id.etInfo2);
        etCliente = (EditText)findViewById(R.id.etCliente);
        etPedido = (EditText)findViewById(R.id.etPedido);

        Intent iResult = new Intent();
        iResult.putExtra("id",etInfo1.getText().toString());
        iResult.putExtra("mesa",etInfo2.getText().toString());
        iResult.putExtra("cliente",etCliente.getText().toString());
        iResult.putExtra("itens",etPedido.getText().toString());

        setResult(RESULT_OK, iResult);
        super.finish();
    }

    public void saveEdit(View v){
        etInfo1 = (EditText)findViewById(R.id.etInfo1);
        etInfo2 = (EditText)findViewById(R.id.etInfo2);
        etCliente = (EditText)findViewById(R.id.etCliente);
        etPedido = (EditText)findViewById(R.id.etPedido);


        super.finish();
    }
    public void goBack(View v){
        finish();
    }
}

Adding the error in LogCat as asked in the comments:

2019-11-20 14:31:10.355 14923-14923/pmd.di.ubi.pedidunchos_2 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: pmd.di.ubi.pedidunchos_2, PID: 14923
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.view.View$DeclaredOnClickListener.onClick(View.java:5652)
        at android.view.View.performClick(View.java:6615)
        at android.view.View.performClickInternal(View.java:6592)
        at android.view.View.access$3100(View.java:786)
        at android.view.View$PerformClick.run(View.java:25951)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:201)
        at android.app.ActivityThread.main(ActivityThread.java:6815)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at android.view.View$DeclaredOnClickListener.onClick(View.java:5647)
        at android.view.View.performClick(View.java:6615) 
        at android.view.View.performClickInternal(View.java:6592) 
        at android.view.View.access$3100(View.java:786) 
        at android.view.View$PerformClick.run(View.java:25951) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:201) 
        at android.app.ActivityThread.main(ActivityThread.java:6815) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873) 
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.ViewParent android.widget.LinearLayout.getParent()' on a null object reference
        at pmd.di.ubi.pedidunchos_2.Main2Activity.deletePedido(Main2Activity.java:134)
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.view.View$DeclaredOnClickListener.onClick(View.java:5647) 
        at android.view.View.performClick(View.java:6615) 
        at android.view.View.performClickInternal(View.java:6592) 
        at android.view.View.access$3100(View.java:786) 
        at android.view.View$PerformClick.run(View.java:25951) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:201) 
        at android.app.ActivityThread.main(ActivityThread.java:6815) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873) 
2019-11-20 14:31:10.437 1371-1830/? E/InputDispatcher: channel '55262fd pmd.di.ubi.pedidunchos_2/pmd.di.ubi.pedidunchos_2.Main2Activity (server)' ~ Channel is unrecoverably broken and will be disposed!
2019-11-20 14:31:10.450 1371-1830/? E/InputDispatcher: channel 'c67c606 pmd.di.ubi.pedidunchos_2/pmd.di.ubi.pedidunchos_2.MainActivity (server)' ~ 
Channel is unrecoverably broken and will be disposed!

Solution

  • Delete method with error I want to make a method to delete a choosen row and update the view of the database table. The method I have, is only deleting it, but with erros. By errors I mean: whenever I click the row I want to delete and then click on the delete button, the app is restarts. And it actually deletes the row from the db.

    The reason why the delete works but then crashes is that you are getting a null pointer exception (NPE) when trying to get the parent view of the calculated/rderived LinearLayout when using ((LinearLayout) oLL1.getParent()).removeView(oLL1); So the row has been deleted.

    If you put a breakpoint on the line ((LinearLayout) oLL1.getParent()).removeView(oLL1); and run in debug mode and attempt a deletion then the debug window will appear and if you use that to inspect the variables you will probably see that oLL1 is null or if not if the you split ((LinearLayout) oLL1.getParent()).removeView(oLL1); into 2 statements that the result of ((LinearLayout) oLL1.getParent()) is null.

    If (my interpretation/assumption is that the onClicked view is a button in the list presented to the user, rather than a single button), as such to remove the row's view (assuming the Linear Layout is the parent of the clicked button) then you should be getting and removing the parent of the clicked button.

    i.e. utilising v that is passed to the deletePedido method.

    However, I believe that you are basically trying to re-invent the proverbial wheel and that matters would be greatly simplified if instead of trying to manage your own List of Views that you used a ListView or a RecyclerView as these manage the handling of the rows in the list of data.

    ListView example

    The following is an example that allows you to insert, edit and delete rows from a table. Noting that insert is mimiced rather than calling another activity edit does nothing but includes comments - in short if you use the id (assuming that it an alias of the rowid)) all that you need to do is pass the id to the activity and it can then retrieve the entire row.

    An attempt has been made to make the example similar to what has been assumed as the goal in you question.

    First the main activities layout :-

    <?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:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/current"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!">
        </TextView>
    
        <ListView
            android:id="@+id/olv1"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:layout_height="0dp">
        </ListView>
    
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <Button
                android:id="@+id/insert"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="ADD NEW">
            </Button>
            <Button
                android:id="@+id/delete"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="DELETE SELECTED">
            </Button>
            <Button
                android:id="@+id/edit"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="EDIT SELECTED">
            </Button>
        </LinearLayout>
    </LinearLayout>
    

    The TextView current is used to display the currently selected row (clicking a row will selected that row).

    The ListView takes a layout (see MainActivity where the stock SimpleListItem_2 layout has been used for each row). There are three buttons ADD NEW, EDIT SELECTED and DELETE SELECTED.

    All of the Database Access mehods (queries, inserts, deletes and updates) are in the DatabaseHelper class AjudanteParaAbrirBB.java :-

    public class AjudanteParaAbrirBD extends SQLiteOpenHelper {
    
        public static final String DBNAME = "ajudante_para_abrir_BD";
        public static final int DBVERSION = 1;
        public static final String TABLE_NAME = "mytable";
        public static final String COL_ID = BaseColumns._ID;
        public static final String COL_ACAO = "acao";
        public static final String COL_COUNT = "count";
        public static final String COl_CURRENTTIME = "currentTime";
        public AjudanteParaAbrirBD(Context context) {
            super(context, DBNAME, null, DBVERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            String crt_table_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME +
                    "(" +
                    COL_ID + " INTEGER PRIMARY KEY, " +
                    COL_ACAO + " TEXT," +
                    COL_COUNT + " INTEGER, " +
                    COl_CURRENTTIME + " TEXT " +
                    ")";
            db.execSQL(crt_table_sql);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    
        public long insert(String acao, long count, String currentTime) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues cv = new ContentValues();
            cv.put(COL_ACAO,acao);
            cv.put(COL_COUNT,count);
            cv.put(COl_CURRENTTIME,currentTime);
            return db.insert(TABLE_NAME,null,cv);
        }
    
        public int update(long id, String acao, long count, String currentTime) {
            int rv = -1;
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues cv = new ContentValues();
            if (acao != null && acao.length() > 0) {
                cv.put(COL_ACAO,acao);
            }
            if (count > -1) {
                cv.put(COL_COUNT,count);
            }
            if (currentTime != null && currentTime.length() > 0) {
                cv.put(COl_CURRENTTIME,currentTime);
            }
            if (cv.size() > 0) {
                rv = db.update(
                        TABLE_NAME,cv,
                        COL_ID + "=?",
                        new String[]{String.valueOf(id)}
                        );
            }
            return rv;
        }
    
        public int delete(long id) {
            SQLiteDatabase db = this.getWritableDatabase();
            return db.delete(
                    TABLE_NAME,
                    COL_ID + "=?",
                    new String[]{String.valueOf(id)}
                    );
        }
    
        public Cursor getAll() {
            SQLiteDatabase db = this.getWritableDatabase();
            return db.query(
                    TABLE_NAME,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null
            );
        }
    
        public String getCurrentSelection(long id) {
            String rv = "Nothing to Select";
            String derivedColumn = "dr";
            SQLiteDatabase db = this.getWritableDatabase();
            Cursor csr = db.query(TABLE_NAME,new String[]{
                    "'ACAO is '||" + COL_ACAO +
                            "||' Count='||" + COL_COUNT +
                            "||' CurrentTime='||" + COl_CURRENTTIME +
                            "||' ID='||" + COL_ID +
                            " AS " + derivedColumn},
                    COL_ID + "=?",new String[]{String.valueOf(id)},
                    null,null,null
            );
            if (csr.moveToFirst()) {
                rv = csr.getString(csr.getColumnIndex(derivedColumn));
            }
            csr.close();
            return rv;
        }
    }
    
    • You should see that some of the code resembles some of you code used in activities. However, the methods can be called from anywhere where you have an instance of AjudanteParaAbrirBB.

    Putting it all together is MainActicity.java :-

    public class MainActivity extends AppCompatActivity {
    
        ListView dblist;
        TextView currentSelection;
        Button add,edit,delete;
        SimpleCursorAdapter adapter;
        AjudanteParaAbrirBD DBHelper;
        Cursor csr;
        long current_id = -1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            currentSelection = this.findViewById(R.id.current);
            dblist = this.findViewById(R.id.olv1);
            add = this.findViewById(R.id.insert);
            edit = this.findViewById(R.id.edit);
            delete = this.findViewById(R.id.delete);
            DBHelper = new AjudanteParaAbrirBD(this);
            setupButtons();
            manageListView();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            manageListView(); //<<<<<<<<<< refreshes the ListView
        }
    
        private void manageListView() {
            edit.setVisibility(View.INVISIBLE);
            delete.setVisibility(View.INVISIBLE);
            csr = DBHelper.getAll();
            if (csr.getCount() > 0) {
                if (csr.moveToFirst()) {
                    current_id = csr.getLong(csr.getColumnIndex(AjudanteParaAbrirBD.COL_ID));
                    currentSelection.setText(
                            DBHelper.getCurrentSelection(current_id)
                    );
                    csr.moveToPosition(-1);
                }
                edit.setVisibility(View.VISIBLE);
                delete.setVisibility(View.VISIBLE);
            }
            if (adapter == null) {
                adapter = new SimpleCursorAdapter(
                        this,
                        android.R.layout.simple_expandable_list_item_2,
                        csr,
                        new String[]{AjudanteParaAbrirBD.COL_ACAO, AjudanteParaAbrirBD.COl_CURRENTTIME},
                        new int[]{android.R.id.text1, android.R.id.text2},
                        0
                );
                dblist.setAdapter(adapter);
                dblist.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                        current_id = id;
                        currentSelection.setText(DBHelper.getCurrentSelection(id));
                    }
                });
            } else {
                adapter.swapCursor(csr);
            }
        }
    
        private void setupButtons() {
            add.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    handleInsertButtonClicked();
                }
            });
            delete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    handleDeleteButtonClicked(current_id);
                }
            });
            edit.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    handleEditButtonClicked(current_id);
                }
            });
        }
    
        private void handleInsertButtonClicked() {
            //...... instantiate intent and startactivity to insert row
            // HOWEVER for demo
            DBHelper.insert("XX",99,"2019-01-01");
            manageListView(); //<<<<<< note only needed because onResume is not called
    
            // onResume would be called when the started activity is finished
        }
    
        private void handleEditButtonClicked(long id) {
            //...... instantiate intent, add intent extra for id, and then startactivity to edit
        }
    
        private void handleDeleteButtonClicked(long id) {
            // No need to start and activity just delete and refresh list
            DBHelper.delete(id);
            manageListView();
        }
    }
    

    Result/Demo

    When first run there is no data and thus only the ADD NEW button is available, as per :-

    enter image description here

    Rather than write an insert activity clicking the ADD NEW button inserts a row (allbeit that the data is the same EXCEPT for the id). The first time the ADD NEW button is clicked the DELETE SELECTED and EDIT SELECTED buttons are visible. Additionally the TextView at the top displays the information for that row.

    After clicking ADD NEW another three times then 4 rows are displayed :-

    enter image description here

    If a row is clicked (e.g. the second) then the data for that row is displayed at the top. Noting that the id changes.

    If say the 2nd row is clicked and then the DELETE SELECTED buttons is clicked then the row is deleted from the database and the ListView shows just the 3 remaining rows.

    e.g.

    enter image description here

    Clicking Edit does nothing. However, the comments in the code indicate how to manage editing and inserting via activities.