1

I am currently working on an Android Studio application that utilizes SQLite database that can store customer records of a car purchase, and the app stops as soon as the ADD button is clicked. Apparently, there is a null object reference. The app has a MainActivity that has an add button to allow the user the add customer database entries. When clicked the app should go to the CustomerRcrdActivity to allow database entries, from there the user should be able to add, view, update, or delete entries all why the entry should display on the screen.

I am learning how to create apps for Android, so please be kind. I am not asking for you to do anything for me, but please kindly point me in the correct direction. I have searched everywhere for input but fear I am overlooking a logic error, I could use a second set of eyes.

Any input or tips on the matter would be much appreciated.

Thank you very much,

Below is the code I suspect could be at fault, (I will include the XML just in case I have done something wonky).

content_main.xml (it includes the listview I would like to update upon adding an entry) If there is a simpler way, I am not set in stone.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">

<TextView
    android:id="@+id/ContentMain_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:fontFamily="sans-serif-smallcaps"
    android:text="@string/content_main_title"
    android:textAlignment="center"
    android:textSize="20sp"
    android:textStyle="bold"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.027" />

<Button
    android:id="@+id/button_CR_add"
    android:layout_width="112dp"
    android:layout_height="wrap_content"
    android:layout_marginBottom="416dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="7dp"
    android:onClick="buttonClicked"
    android:text="@string/ContentMain_button_CD_add"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.054"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/ContentMain_title"
    app:layout_constraintVertical_bias="0.022" />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="486dp"
    android:layout_marginTop="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/button_CR_add">

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

</LinearLayout>
</android.support.constraint.ConstraintLayout>

The XML for the CustomerRcrdActivity is ommited

CustomerRcrdActivity.java

public class CustomerRcrdActivity extends AppCompatActivity
{

private EditText FName, LName, CarMake, CarModel, CarCost;
private String F_Name;
private String L_Name;
private String Car_Make;
private String Car_Model;
private double d_Car_Cost;
private DatabaseHandler dh;
private dataAdapter da;
private Customer dataModel;
private ListView lv;
private long mId;

//<<<<<<<<<< new class variables
private String original_FName, original_LName, original_CarMake, original_CarModel, original_CarCost;
private boolean mLoaded = false;
private Button mUpdateButton;
private Button mAddButton;
private Button mDeleteButton;
private ArrayList<Customer> mCustomers = new ArrayList<>();
private List<EditText> mAlleditTexts = new ArrayList<>();

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

    // GET Intent THAT STARTED ACTIVITY
    Intent intent = getIntent();

    // INSTANTIATE DATABASE HANDLER
    dh = new DatabaseHandler(this);

    FName = findViewById(R.id.editText2_FName);
    LName = findViewById(R.id.editText2_LName);
    CarMake = findViewById(R.id.editText2_CarMake);
    CarModel = findViewById(R.id.editText2_CarModel);
    CarCost = findViewById(R.id.editText2_CarCost);
    lv = findViewById(R.id.listView);
    //<<<<<<<<<<< ADDED
    mAlleditTexts.add(FName);
    mAlleditTexts.add(LName);
    mAlleditTexts.add(CarMake);
    mAlleditTexts.add(CarModel);
    mAlleditTexts.add(CarCost);

    //<<<<<<<<<< END OF ADDED

    Button mViewButton = findViewById(R.id.button_CR_view);
    mAddButton = findViewById(R.id.button_CR_add);
    mUpdateButton = findViewById(R.id.button_CR_update);
    mDeleteButton = findViewById(R.id.button_CR_delete);

    mViewButton.setVisibility(View.GONE);                        // never Show and free screen space
    mViewButton.setText(R.string.mViewButton_setText);           // Hijack View Button for clear data
    mViewButton.setVisibility(View.VISIBLE);                     // Show the View (now CLEAR)
    mAddButton.setEnabled(false);                                // Can't click Add as nothing to add
    mUpdateButton.setEnabled(false);                             // Can't click Update nothing to update
    mDeleteButton.setEnabled(false);                             // Can't click Delete as nothing to delete
    setOriginalValues();
    ShowRecords();                                               //<<<< Always show the list no need for View button
    setEditTextFocusChangedListener(mAlleditTexts);
}

//<<<<<<<<<< NEW METHOD
private void setEditTextFocusChangedListener(List<EditText> edit_texts)
{
    for (EditText e: edit_texts)
    {
        e.setOnFocusChangeListener(new View.OnFocusChangeListener()
        {
            @Override
            public void onFocusChange(View view, boolean b)
            {
                if (areOriginalValuesChanged())
                {
                    if (mLoaded)
                    {
                        mUpdateButton.setEnabled(true);
                        mDeleteButton.setEnabled(false);
                        mAddButton.setEnabled(false);
                    } else
                    {

                    }
                } else
                {
                    if (!mLoaded)
                    {
                        mAddButton.setEnabled(true);
                    } else
                    {
                        mAddButton.setEnabled(false);
                    }
                }
            }
        });
    }
}

//<<<<<<<<<< NEW METHOD
private void setOriginalValues()
{
    original_FName = FName.getText().toString();
    original_LName = LName.getText().toString();
    original_CarMake = CarMake.getText().toString();
    original_CarModel = CarModel.getText().toString();
    original_CarCost = CarCost.getText().toString();
}

//<<<<<<<<<< NEW METHOD
private boolean areOriginalValuesChanged()
{
    /*if (original_FName.equals(FName.getText().toString())
            && original_LName.equals(LName.getText().toString())
            && original_CarMake.equals(CarMake.getText().toString())
            && original_CarModel.equals(CarModel.getText().toString())
            && original_CarCost.equals(CarCost.getText().toString())
            )
    {
        return false;
    }
    return true;*/
    return !original_FName.equals(FName.getText().toString())
            || !original_LName.equals(LName.getText().toString())
            || !original_CarMake.equals(CarMake.getText().toString())
            || !original_CarModel.equals(CarModel.getText().toString())
            || !original_CarCost.equals(CarCost.getText().toString());
}

//<<<<<<<<<< NEW METHOD
private void clearEditTexts(List<EditText> editTexts)
{
    for (EditText e: editTexts)
    {
        e.setText("");
    }
    setOriginalValues();
    mLoaded = false;
}

public void buttonClicked(View view)
{
    int id = view.getId();

    switch (id)
    {
        case R.id.button_CR_update:
            // CALL updateCustomer() TO UPDATE CUSTOMER RECORDS
            updateCustomer();
            mLoaded = false;
            break;

        case R.id.button_CR_add:
            // CALL addCustomer() TO ADD TO DATABASE
            addCustomer();
            // CLEAR FIELDS
            clearEditTexts(mAlleditTexts);
            mLoaded = false;
            break;

        case R.id.button_CR_delete:
            // CALL deleteCustomer() TO DELETE CUSTOMER RECORD
            deleteCustomer();
            break;

        case R.id.button_CR_view:
            // CLEAR FIELDS
            clearEditTexts(mAlleditTexts);
            mLoaded = false;
            mAddButton.setEnabled(true);
            mUpdateButton.setEnabled(false);
            mDeleteButton.setEnabled(false);
            break;
    }
    // UPDATE LIST IRRESPECTIVE OF BUTTON CLICKED
    ShowRecords();
}

// UPDATE CUSTOMER RECORD
private void updateCustomer()
{
    getValues();
    if (dh.updateCustomer(dataModel, mId)>0)
    {
        Toast.makeText(getApplicationContext(), "Customer Updated Successfully", Toast.LENGTH_LONG).show();
    } else
    {
        Toast.makeText(getApplicationContext(), "Customer Not Updated", Toast.LENGTH_LONG).show();
    }
}
// INSERT DATA INTO DATABASE
private void addCustomer()
{
    boolean ok_to_add = true;
    // GET VALUES FROM EditText
    getValues();
    if (F_Name == null || F_Name.length() < 1) ok_to_add = false;
    if (L_Name == null || L_Name.length() < 1) ok_to_add = false;
    if (Car_Make == null || Car_Make.length() < 1) ok_to_add = false;
    if (Car_Model == null || Car_Model.length() < 1) ok_to_add = false;
    if (ok_to_add)
    {
        dh.addCustomers(new Customer(F_Name, L_Name, Car_Make, Car_Model, d_Car_Cost));
        Toast.makeText(getApplicationContext(), "Customer Added Successfully", Toast.LENGTH_LONG).show();
    } else
    {
        Toast.makeText(getApplicationContext(), "Unable To Add - Some Data hasn't been given", Toast.LENGTH_LONG).show();
    }
}
// DELETE CUSTOMER RECORD
private void deleteCustomer()
{
    getValues();
    if (dh.deleteCustomer(mId))
    {
        Toast.makeText(getApplicationContext(), "Customer Deleted Successfully", Toast.LENGTH_LONG).show();
    } else
    {
        Toast.makeText(getApplicationContext(), "Customer Not Deleted", Toast.LENGTH_LONG).show();
    }
}

// FUNCTION TO GET VALUES FROM EditText
private void getValues()
{
    F_Name = FName.getText().toString();
    L_Name = LName.getText().toString();
    Car_Make = CarMake.getText().toString();
    Car_Model = CarModel.getText().toString();
    String car_Cost = CarCost.getText().toString();
    if (car_Cost.length() < 1)
    {
        car_Cost = "0.00";
    }
    d_Car_Cost = Double.parseDouble(car_Cost);

}
// RETRIEVE DATA FROM DATABASE & SET TO LIST VIEW
//<<<<<<<<<< CHANGED QUITE A BIT
// Introduced single Customer List with class scope rather than create new list and adapter every time
//      i.e. mCustomers
// Always clear mCustomers and rebuild from database
// if da (the adapter) is null and therefore hasn't been instantiated, instantiate it just once
// otherwise always notify the adapter that the data (mCustomer) has changed
// Added code to the Listener (added just the once now) to set the edit text's to the values
// of the clicked item in the list (load the data)
//  setting flag to say that the data has just been loaded.
//  also setting the original values to the new data
private void ShowRecords()
{
    mCustomers.clear();
    mCustomers.addAll(dh.getAllCustomers());

    if (da == null)
    {
        da = new dataAdapter(this, mCustomers);
        lv.setAdapter(da);

        lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
        {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
            {
                dataModel = mCustomers.get(position);
                //<<<<<<<<<< Added
                FName.setText(dataModel.getFName());
                LName.setText(dataModel.getLName());
                CarMake.setText(dataModel.get_CarMake());
                CarModel.setText(dataModel.get_CarModel());
                CarCost.setText(String.valueOf(dataModel.get_CarCost()));
                mLoaded = true;
                setOriginalValues();
                mDeleteButton.setEnabled(true);
                mUpdateButton.setEnabled(false);
                mAddButton.setEnabled(false);
                mId = dataModel.getID();
                //<<<<<<<<<< End of Added
                Toast.makeText(getApplicationContext(), String.valueOf(dataModel.getID()), Toast.LENGTH_SHORT).show();
            }
        });
    } else
    {
        da.notifyDataSetChanged();
    }
}
}

DatabaseHandler.java

public class DatabaseHandler extends SQLiteOpenHelper
{
// DATABASE VERSION
private static final int DATABASE_VERSION = 1;
// DATABASE NAME
private static final String DATABASE_NAME = "CARDEALER.db";
// CUSTOMER TABLE NAME
public static final String TABLE_CUSTOMERS = "Customers";


// CUSTOMER TABLE COLUMN NAMES
private static final String KEY_ID = "ID";
private static final String KEY_FNAME = "First";
private static final String KEY_LNAME = "Last";
private static final String KEY_CAR_MAKE = "Make";
private static final String KEY_CAR_MODEL = "Model";
private static final String KEY_CAR_COST = "Cost";

DatabaseHandler(Context context)
{
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// CREATE TABLES
@Override
public void onCreate(SQLiteDatabase dh)
{
    String CREATE_TABLE_CUSTOMERS = "CREATE TABLE " + TABLE_CUSTOMERS + "("
            + KEY_ID +" INTEGER PRIMARY KEY,"
            + KEY_FNAME +" TEXT,"
            + KEY_LNAME +" TEXT,"
            + KEY_CAR_MAKE  +" TEXT,"
            + KEY_CAR_MODEL +" TEXT,"
            + KEY_CAR_COST +" NUMERIC" + ")";
    dh.execSQL(CREATE_TABLE_CUSTOMERS);
}
// UPGRADING DATABASE
@Override
public void onUpgrade(SQLiteDatabase dh, int oldVersion, int newVersion)
{
    // DROP OLDER TABLE IF EXISTED
    dh.execSQL("DROP TABLE IF EXISTS " + TABLE_CUSTOMERS);

    // CREATE TABLES AGAIN
    onCreate(dh);
}
/**
 * All CRUD (CREATE, READ, UPDATE, DELETE) OPERATIONS
 */
// INSERT VALUES TO TABLE CUSTOMERS
public void addCustomers(Customer customer)
{
    SQLiteDatabase dh = this.getReadableDatabase();
    ContentValues values = new ContentValues();

    values.put(KEY_FNAME, customer.getFName());
    values.put(KEY_LNAME, customer.getLName() );
    values.put(KEY_CAR_MAKE, customer.get_CarMake());
    values.put(KEY_CAR_MODEL, customer.get_CarModel());
    values.put(KEY_CAR_COST, customer.get_CarCost());

    dh.insert(TABLE_CUSTOMERS, null, values);
    dh.close();
}
/**
 *GETTING ALL CUSTOMERS
 **/
public List<Customer> getAllCustomers()
{
    List<Customer> customerList = new ArrayList<Customer>();
    // SELECT ALL QUERY
    String selectQuery = "SELECT  * FROM " + TABLE_CUSTOMERS;

    SQLiteDatabase dh = this.getWritableDatabase();
    Cursor cursor = dh.rawQuery(selectQuery, null);

    // LOOP THROUGH ALL ROWS & ADD TO LIST
    if (cursor.moveToFirst())
    {
        do {
            Customer customer = new Customer();
            customer.setID(Integer.parseInt(cursor.getString(0)));
            customer.setFName(cursor.getString(1));
            customer.setLName(cursor.getString(2));
            customer.set_CarMake(cursor.getString(3));
            customer.set_CarModel(cursor.getString(4));
            customer.set_CarCost(cursor.getDouble(5));

            // ADDING CUSTOMER TO LIST
            customerList.add(customer);
        } while (cursor.moveToNext());
    }
    // CLOSE OUT RAW QUERY CALL
    cursor.close();
    // RETURN CUSTOMER LIST
    return customerList;
}
/**
 *UPDATING SINGLE CUSTOMER
 **/
public int updateCustomer(Customer customer, long id)
{
    if (customer == null)
    {
        return 0;
    }
    SQLiteDatabase dh = this.getWritableDatabase();

    ContentValues values = new ContentValues();
    values.put(KEY_FNAME, customer.getFName());
    values.put(KEY_LNAME, customer.getLName());
    values.put(KEY_CAR_MAKE, customer.get_CarMake());
    values.put(KEY_CAR_MODEL, customer.get_CarModel());
    values.put(KEY_CAR_COST, customer.get_CarCost());

    // UPDATING ROW
    return dh.update(TABLE_CUSTOMERS, values, KEY_ID + " = ?",
            new String[] { String.valueOf(id) });
}
/**
 *DELETING SINGLE CUSTOMER
 **/
public boolean deleteCustomer(long Id)
{
    boolean rv = false;
    SQLiteDatabase dh = this.getWritableDatabase();
    rv = (dh.delete(TABLE_CUSTOMERS, KEY_ID + " = ?",
            new String[] { String.valueOf(Id) }) > 0);
    dh.close();
    return rv;
}
}

dataAdapter.java

public class dataAdapter extends ArrayAdapter<Customer>
{


public dataAdapter(Context context, ArrayList<Customer> customers)
{
    super(context, R.layout.list_customers, customers);
    Context context1 = context;
    ArrayList<Customer> mcustomer = customers;
}

public  class  Holder
{
    TextView idV;
    TextView nameFV;
    TextView nameLV;
    TextView carmakeV;
    TextView carmodelV;
    TextView carcostV;
}

@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent)
{
    // GET DATA ITEM FOR THIS POSITION

    Customer data = getItem(position);
    // CHECK IF EXISTING VIEW IS BEING REUSED, OTHERWISE INFLATE VIEW

    // VIEW LOOKUP CACHE STORED IN TAG
    Holder viewHolder;

    if (convertView == null)
    {
        viewHolder = new Holder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.list_customers, parent, false);

        viewHolder.idV = (TextView) convertView.findViewById(R.id.textView3_CustomerID);
        viewHolder.nameFV = (TextView) convertView.findViewById(R.id.textView3_FName);
        viewHolder.nameLV = (TextView) convertView.findViewById(R.id.textView3_LName);
        viewHolder.carmakeV = (TextView) convertView.findViewById(R.id.textView3_CarMake);
        viewHolder.carmodelV = (TextView) convertView.findViewById(R.id.textView3_CarModel);
        viewHolder.carcostV = (TextView) convertView.findViewById(R.id.textView3_CarCost);

        convertView.setTag(viewHolder);
    } else
    {
        viewHolder = (Holder) convertView.getTag();
    }

    viewHolder.nameFV.setText("First Name: " + data.getFName());
    viewHolder.nameLV.setText("Last Name: " + data.getLName());
    viewHolder.carmakeV.setText("Car Make: " + data.get_CarMake());
    viewHolder.carmodelV.setText("Car Model: " + data.get_CarModel());
    viewHolder.carcostV.setText("Car Cost: " + data.get_CarCost());

    // RETURN COMPLETED VIEW TO RENDER ON SCREEN
    return convertView;
}
}

I am including MainActivity in case I have missed something there. Here is MainActivity.java

public class MainActivity extends AppCompatActivity
{
//public ListView lv;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    // even tried ListView here (R.id.listView....)
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View view)
        {
            Snackbar.make(view, "Please, click ADD button to begin! ", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    if (id == R.id.action_about)
    {
        // possibly go to another page
        Toast.makeText(MainActivity.this,
                "SQLite Database App", Toast.LENGTH_LONG).show();
        return true;
    }

    return super.onOptionsItemSelected(item);
}

public void buttonClicked(View view)
{
    // START NEW ACTIVITY WHEN ADD BUTTON CLICKED
    Intent intent = new Intent(this, CustomerRcrdActivity.class);
    startActivity(intent);


}
}

Here is the Logcat log:

05-30 09:02:03.253 30145-30145/edu.phoenix.mbl402.week4apppp1943_rev1 E/AndroidRuntime: FATAL EXCEPTION: main
Process: edu.phoenix.mbl402.week4apppp1943_rev1, PID: 30145
java.lang.RuntimeException: Unable to start activity ComponentInfo{edu.phoenix.mbl402.week4apppp1943_rev1/edu.phoenix.mbl402.week4apppp1943_rev1.CustomerRcrdActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ListView.setAdapter(android.widget.ListAdapter)' on a null object reference
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
    at android.app.ActivityThread.-wrap11(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ListView.setAdapter(android.widget.ListAdapter)' on a null object reference
    at edu.phoenix.mbl402.week4apppp1943_rev1.CustomerRcrdActivity.ShowRecords(CustomerRcrdActivity.java:276)
    at edu.phoenix.mbl402.week4apppp1943_rev1.CustomerRcrdActivity.onCreate(CustomerRcrdActivity.java:79)
    at android.app.Activity.performCreate(Activity.java:6975)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
    at android.app.ActivityThread.-wrap11(Unknown Source:0) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
    at android.os.Handler.dispatchMessage(Handler.java:105) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.app.ActivityThread.main(ActivityThread.java:6541) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

Also, I have included the sugested modifications, thank you again!

Potter
  • 15
  • 7

2 Answers2

1

Example with some improvements/considerations (perhaps)

The code that follows may give you some ideas/techniques and is based upon the Answers (I'll include all code which should help you get around the App crashing on start).

The first improvment is to load the ListView when the Activity is started. This was pretty simple, just call the ShowRecords method. This does away with the need for the View button (see later as it's been hi-jacked).

However, the ShowRecords method has been drastically changed. To incoporate single instances of the Adapter and the source Customer List.

Some logic/intelligence(sic) has been added to the buttons. Those not applicable are greyed out. - Initially you can't delete or update nothing, you can only Add (add not add it any data is omitted). - If you click on an item in the List then that data is loaded into the EditTexts and the Delete button is the only button that can be clicked. - If you change a value in an EditText, then only the Update button can be clicked.

All actions work and also update the list (when available according to the logic above). So records can be added, updated and deleted.

The View button has been hi-jacked to now be the CLEAR button. This is always available and will clear the EditTexts and set the Add button as being available (clicking on a list item still loads that record's data).

To facilitate the above some new class variables have been defined and also some have been moved from being method variables to class variables e.g. the Adapater and the Customer List (you had da declared already so this was used).

The vast majority of the changes are just to the CustomerRcrdActivity although some minor changes have been made to the DatabaseHelper (long for the id, which is the correct practice as an id can exceed the maximum value of an int).

Anyway here's the code :-

CustomerRcrdActivity :-

public class CustomerRcrdActivity extends AppCompatActivity {

    private EditText FName, LName, CarMake, CarModel, CarCost;
    private String F_Name;
    private String L_Name;
    private String Car_Make;
    private String Car_Model;
    private double d_Car_Cost;
    private DatabaseHandler dh;
    private dataAdapter da;
    private Customer dataModel;
    private ListView lv;
    private long mId;

    //<<<<<<<<<< new class variables
    private String original_FName, original_LName, original_CarMake, original_CarModel, original_CarCost;
    private boolean mLoaded = false;
    private Button mViewButton, mUpdateButton, mAddButton, mDeleteButton;
    private ArrayList<Customer> mCustomers = new ArrayList<>();
    private List<EditText> mAlleditTexts = new ArrayList<>();

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

        // GET Intent THAT STARTED ACTIVITY
        Intent intent = getIntent();

        // INSTANTIATE DATABASE HANDLER
        dh = new DatabaseHandler(this);

        FName = (EditText) findViewById(R.id.editText2_FName);
        LName = (EditText) findViewById(R.id.editText2_LName);
        CarMake = (EditText) findViewById(R.id.editText2_CarMake);
        CarModel = (EditText) findViewById(R.id.editText2_CarModel);
        CarCost = (EditText) findViewById(R.id.editText2_CarCost);

        //<<<<<<<<<<< ADDED
        mAlleditTexts.add(FName);
        mAlleditTexts.add(LName);
        mAlleditTexts.add(CarMake);
        mAlleditTexts.add(CarModel);
        mAlleditTexts.add(CarCost);
        lv = (ListView) findViewById(R.id.lv);
        //<<<<<<<<<< END OF ADDED

        mViewButton = (Button) findViewById(R.id.button_CR_view);
        mAddButton = (Button) findViewById(R.id.button_CR_add);
        mUpdateButton = (Button) findViewById(R.id.button_CR_update);
        mDeleteButton = (Button) findViewById(R.id.button_CR_delete);

        mViewButton.setVisibility(View.GONE); // never Show and free screen space
        mViewButton.setText("CLEAR"); // Hijack View Button for clear data
        mViewButton.setVisibility(View.VISIBLE); // Show the View (now CLEAR)
        mAddButton.setEnabled(false); // Can't click Add as nothing to add
        mUpdateButton.setEnabled(false); // Can't click Update nothing to update
        mDeleteButton.setEnabled(false); // Can't click Delete as nothing to delete
        setOriginalValues();
        ShowRecords(); //<<<< Always show the list no need for View button
        setEditTextFocusChangedListener(mAlleditTexts);
    }

    //<<<<<<<<<< NEW METHOD
    private void setEditTextFocusChangedListener(List<EditText> edit_texts) {
        for (EditText e: edit_texts) {

            e.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View view, boolean b) {
                    if (areOriginalValuesChanged()) {
                        if (mLoaded) {
                            mUpdateButton.setEnabled(true);
                            mDeleteButton.setEnabled(false);
                            mAddButton.setEnabled(false);
                        } else {

                        }
                    } else {
                        if (!mLoaded) {
                            mAddButton.setEnabled(true);
                        } else {
                            mAddButton.setEnabled(false);
                        }
                    }
                }
            });
        }
    }

    //<<<<<<<<<< NEW METHOD
    private void setOriginalValues() {
        original_FName = FName.getText().toString();
        original_LName = LName.getText().toString();
        original_CarMake = CarMake.getText().toString();
        original_CarModel = CarModel.getText().toString();
        original_CarCost = CarCost.getText().toString();
    }

    //<<<<<<<<<< NEW METHOD
    private boolean areOriginalValuesChanged() {
        if (original_FName.equals(FName.getText().toString())
                && original_LName.equals(LName.getText().toString())
                && original_CarMake.equals(CarMake.getText().toString())
                && original_CarModel.equals(CarModel.getText().toString())
                && original_CarCost.equals(CarCost.getText().toString())
                ) {
            return false;
        }
        return true;
    }

    //<<<<<<<<<< NEW METHOD
    private void clearEditTexts(List<EditText> editTexts) {
        for (EditText e: editTexts) {
            e.setText("");
        }
        setOriginalValues();
        mLoaded = false;
    }

    public void buttonClicked(View view)
    {
        int id = view.getId();

        switch (id)
        {
            case R.id.button_CR_update:
                // CALL updateCustomer() TO UPDATE CUSTOMER RECORDS
                updateCustomer();
                mLoaded = false;
                break;

            case R.id.button_CR_add:
                // CALL addCustomer() TO ADD TO DATABASE
                addCustomer();
                clearEditTexts(mAlleditTexts);
                mLoaded = false;

                // START NEW ACTIVITY WHEN ADD BUTTON CLICKED
                //Intent intent = new Intent(this, DatabaseHandler.class);
                //startActivity(intent);

                break;

            case R.id.button_CR_delete:
                // CALL deleteCustomer() TO DELETE CUSTOMER RECORD
                deleteCustomer();
                break;

            case R.id.button_CR_view:
                // CALL viewCustomer() TO VIEW CUSTOMER RECORD
                // START NEW ACTIVITY WHEN ADD BUTTON CLICKED
                //Intent intent2 = new Intent(this, CustomerDBActivity.class);
                //startActivity(intent2);

                // CALL showRecords() TO VIEW CUSTOMER RECORD VIA ListView
                clearEditTexts(mAlleditTexts);
                mLoaded = false;
                mAddButton.setEnabled(true);
                mUpdateButton.setEnabled(false);
                mDeleteButton.setEnabled(false);
                break;
        }
        ShowRecords(); // Update the List irrespective of button clicked
    }


    private void updateCustomer()
    {
        getValues();
         if (dh.updateCustomer(dataModel, mId)>0) {
             Toast.makeText(getApplicationContext(), "Customer Updated Successfully", Toast.LENGTH_LONG).show();
         } else {
             Toast.makeText(getApplicationContext(), "Customer Not Updated", Toast.LENGTH_LONG).show();
         }
    }
    // INSERT DATA INTO DATABASE
    private void addCustomer()
    {
        boolean ok_to_add = true;
        getValues();
        if (F_Name == null || F_Name.length() < 1) ok_to_add = false;
        if (L_Name == null || L_Name.length() < 1) ok_to_add = false;
        if (Car_Make == null || Car_Make.length() < 1) ok_to_add = false;
        if (Car_Model == null || Car_Model.length() < 1) ok_to_add = false;
        if (ok_to_add) {
            dh.addCustomers(new Customer(F_Name, L_Name, Car_Make, Car_Model, d_Car_Cost));
            Toast.makeText(getApplicationContext(), "Customer Added Successfully", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(getApplicationContext(), "Unable To Add - Some Data hasn't been given", Toast.LENGTH_LONG).show();
        }
    }

    private void deleteCustomer()
    {
        getValues();
         if (dh.deleteCustomer(mId)) {
             Toast.makeText(getApplicationContext(), "Customer Deleted Successfully", Toast.LENGTH_LONG).show();
         } else {
             Toast.makeText(getApplicationContext(), "Customer Not Deleted", Toast.LENGTH_LONG).show();
         }
    }

    // FUNCTION TO GET VALUES FROM EditText
    private void getValues()
    {

        F_Name = FName.getText().toString();
        L_Name = LName.getText().toString();
        Car_Make = CarMake.getText().toString();
        Car_Model = CarModel.getText().toString();
        String car_Cost = CarCost.getText().toString();
        if (car_Cost.length() < 1) {
            car_Cost = "0.00";
        }
        d_Car_Cost = Double.parseDouble(car_Cost);

    }
    // RETRIEVE DATA FROM DATABASE & SET TO LIST VIEW
    //<<<<<<<<<< CHANGED QUITE A BIT
    // Introduced single Customer List with class scope rather than create new list and adapter everytime
    //      i.e. mCustomers
    // Always clear mCustomers and rebuild from database
    // if da (the adapter) is null and therefore hasn't been instantiated, instantiate it just once
    // otherwsie always notify the adapter that the data (mCustomer) has changed
    // Added code to the Listener (added just the once now) to set the edit text's to the values
    // of the clicked item in the list (load the data)
    //  setting flag to say that the data has just been loaded.
    //  also setting the original values to the new data
    private void ShowRecords()
    {
        mCustomers.clear();
        mCustomers.addAll(dh.getAllCustomers());
        //final ArrayList<Customer> customers = new ArrayList<>(dh.getAllCustomers());
        //final Customer customers = new Customer(dh.getAllCustomers());
        if (da == null) {
            da = new dataAdapter(this, mCustomers);
            //final dataAdapter data = new dataAdapter(this, mCustomers); //<<<< Shouldn't create a new adapter everytime

            lv.setAdapter(da);

            lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    dataModel = mCustomers.get(position);
                    //<<<<<<<<<< Added
                    FName.setText(dataModel.getFName());
                    LName.setText(dataModel.getLName());
                    CarMake.setText(dataModel.get_CarMake());
                    CarModel.setText(dataModel.get_CarModel());
                    CarCost.setText(String.valueOf(dataModel.get_CarCost()));
                    mLoaded = true;
                    setOriginalValues();
                    mDeleteButton.setEnabled(true);
                    mUpdateButton.setEnabled(false);
                    mAddButton.setEnabled(false);
                    mId = dataModel.getID();
                    //<<<<<<<<<< End of Added
                    Toast.makeText(getApplicationContext(), String.valueOf(dataModel.getID()), Toast.LENGTH_SHORT).show();
                }
            });
        } else {
            da.notifyDataSetChanged();
        }
    }
}

DataBaseHandler

public class DatabaseHandler extends SQLiteOpenHelper
{
    // DATABASE VERSION
    private static final int DATABASE_VERSION = 1;
    // DATABASE NAME
    private static final String DATABASE_NAME = "Car Dealer";
    // PREFERRED THE NAME "STAR DEALERSHIP CUSTOMER DATABASE"
// CUSTOMER TABLE NAME
    public static final String TABLE_CUSTOMERS = "Customers";


    // CUSTOMER TABLE COLUMN NAMES
    private static final String KEY_ID = "ID";
    private static final String KEY_FNAME = "First";
    private static final String KEY_LNAME = "Last";
    private static final String KEY_CAR_MAKE = "Make";
    private static final String KEY_CAR_MODEL = "Model";
    private static final String KEY_CAR_COST = "Cost";

    DatabaseHandler(Context context)
    {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    // CREATE TABLES
    @Override
    public void onCreate(SQLiteDatabase dh)
    {
        String CREATE_TABLE_CUSTOMERS = "CREATE TABLE " + TABLE_CUSTOMERS + "("
                + KEY_ID +" INTEGER PRIMARY KEY,"
                + KEY_FNAME +" TEXT,"
                + KEY_LNAME +" TEXT,"
                + KEY_CAR_MAKE  +" TEXT,"
                + KEY_CAR_MODEL +" TEXT,"
                + KEY_CAR_COST +" NUMERIC" + ")";
        dh.execSQL(CREATE_TABLE_CUSTOMERS);
    }
    // UPGRADING DATABASE
    @Override
    public void onUpgrade(SQLiteDatabase dh, int oldVersion, int newVersion)
    {
        // DROP OLDER TABLE IF EXISTED
        dh.execSQL("DROP TABLE IF EXISTS " + TABLE_CUSTOMERS);

        // CREATE TABLES AGAIN
        onCreate(dh);
    }
    /**
     * All CRUD (CREATE, READ, UPDATE, DELETE) OPERATIONS
     */
// INSERT VALUES TO TABLE CUSTOMERS

    public void addCustomers(Customer customer)
    {
        SQLiteDatabase dh = this.getReadableDatabase();
        ContentValues values = new ContentValues();

        values.put(KEY_FNAME, customer.getFName());
        values.put(KEY_LNAME, customer.getLName() );
        values.put(KEY_CAR_MAKE, customer.get_CarMake());
        values.put(KEY_CAR_MODEL, customer.get_CarModel());
        values.put(KEY_CAR_COST, customer.get_CarCost());

        dh.insert(TABLE_CUSTOMERS, null, values);
        dh.close();
    }
    /**
     *GETTING ALL CUSTOMERS
     **/
    public List<Customer> getAllCustomers()
    {
        List<Customer> customerList = new ArrayList<Customer>();
        // SELECT ALL QUERY
        String selectQuery = "SELECT  * FROM " + TABLE_CUSTOMERS;

        SQLiteDatabase dh = this.getWritableDatabase();
        Cursor cursor = dh.rawQuery(selectQuery, null);

        // LOOP THROUGH ALL ROWS & ADD TO LIST
        if (cursor.moveToFirst())
        {
            do {
                Customer customer = new Customer();
                customer.setID(Integer.parseInt(cursor.getString(0)));
                customer.setFName(cursor.getString(1));
                customer.setLName(cursor.getString(2));
                customer.set_CarMake(cursor.getString(3));
                customer.set_CarModel(cursor.getString(4));
                customer.set_CarCost(cursor.getDouble(5));

                // ADDING CUSTOMER TO LIST
                customerList.add(customer);
            } while (cursor.moveToNext());
        }
        // CLOSE OUT RAW QUERY CALL
        cursor.close();
        // RETURN CUSTOMER LIST
        return customerList;
    }
    /**
     *UPDATING SINGLE CUSTOMER
     **/
    public int updateCustomer(Customer customer, long id)
    {
        if (customer == null) {
            return 0;
        }
        SQLiteDatabase dh = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(KEY_FNAME, customer.getFName());
        values.put(KEY_LNAME, customer.getLName());
        values.put(KEY_CAR_MAKE, customer.get_CarMake());
        values.put(KEY_CAR_MODEL, customer.get_CarModel());
        values.put(KEY_CAR_COST, customer.get_CarCost());

        // UPDATING ROW
        return dh.update(TABLE_CUSTOMERS, values, KEY_ID + " = ?",
                new String[] { String.valueOf(id) });
    }
    /**
     *DELETING SINGLE CUSTOMER
     **/
    public boolean deleteCustomer(long Id)
    {
        boolean rv = false;
        SQLiteDatabase dh = this.getWritableDatabase();
        rv = (dh.delete(TABLE_CUSTOMERS, KEY_ID + " = ?",
                new String[] { String.valueOf(Id) }) > 0);
        dh.close();
        return rv;
    }
}

Customer

  • Note this is perhaps quite different to yours, although perhaps pretty close. The main thing to note is that CustomerId is a long, otherwise as far as the CustomerRcrdActivity is concerned it mimics yours.

:-

public class Customer {

    private long CustomerId;
    private String FName;
    private String LName;
    private String CarMake;
    private String CarModel;
    private Double CarCost;

    public Customer(String first_name, String last_name, String make_of_car, String model, Double cost) {
        this.FName = first_name;
        this.LName = last_name;
        this.CarMake = make_of_car;
        this.CarModel = model;
        this.CarCost = cost;
    }

    public Customer() {
    }

    public void setFName(String FName) {
        this.FName = FName;
    }

    public String getFName() {
        return FName;
    }

    public void setLName(String LName) {
        this.LName = LName;
    }

    public String getLName() {
        return LName;
    }

    public void set_CarMake(String carMake) {
        CarMake = carMake;
    }

    public String get_CarMake() {
        return CarMake;
    }

    public void set_CarCost(Double carCost) {
        CarCost = carCost;
    }

    public Double get_CarCost() {
        return CarCost;
    }

    public void set_CarModel(String carModel) {
        CarModel = carModel;
    }

    public String get_CarModel() {
        return CarModel;
    }

    public void setCustomerId(long customerId) {
        CustomerId = customerId;
    }

    public long getCustomerId() {
        return CustomerId;
    }

    public long getID() {
        return this.getCustomerId();
    }

    public void setID(long id) {
        this.setCustomerId(id);
    }
}

Layouts The layouts should all mimic yours, so no need for them to be included (yours would likely be much better as the ones used for this are minimal).

MainActivity

This does nothing other than start the CustomerRcrdActivity and as such is very basic just :-

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent i = new Intent(this,CustomerRcrdActivity.class);
        startActivity(i);
    }
}
  • Note using back from the CustomerRcrdActivity will take you to an empty Screen and you can do nothing else.

The layout is the basic single TextView generated when creating a Project :-

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

AndroidManiFest.xml

Again this is pretty basic, it has just been amended to have an activity section for the CustomerRcrdActivity :-

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".CustomerRcrdActivity">
    </activity>
</application>

Note the package name (3rd line) will likely not be correct if you used the above, so it would need to be changed.

MikeT
  • 51,415
  • 16
  • 49
  • 68
  • You have been amazing, thank you so much. I have amended my files to reflect the changes you mentioned, but now I am getting a null object reference again at the ListView in CustomerRcrdActivity (java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ListView.setAdapter(android.widget.ListAdapter)'. I have it set when customer clicks the ADD button on the main screen they are taken to customer record activity to add info, but the program crashes as soon as the ADD button is clicked. Any idea what is going on there? – Potter May 30 '18 at 08:29
  • Do you have `private ListView lv;` at the top (pretty sure you would else wouldn't compile). and also `lv = (ListView) findViewById(R.id.lv);` (in `onCreate` method) this is crucial and must be called before trying to access the ListView (set the Adapter). It this last line isn't called then `lv` will still be null. – MikeT May 30 '18 at 08:45
  • Not sure I read correctly read is the Add button on the Main Screen? Perhaps edit your question to include the stack-trace/log cat. – MikeT May 30 '18 at 08:52
  • Just had a thought I've coded `lv = (ListView) findViewById(R.id.lv);` I forgot to say that you had `lv = (ListView) findViewById(R.id.list1);` so it might be you should be using `R.id.list1` – MikeT May 30 '18 at 09:13
  • I had changed that, I even tried changing the id on the list to listView as I read that sometimes that is an issue. I posted the code and logcat above. I know I am missing something here, but I am not sure where. – Potter May 30 '18 at 09:20
  • If you are using Android Studio then click over the left of the line `lv.setAdapter(da);` (the grey border) a red dot should appear. Then click the small green arrow with a grey bug underneath to debug. Click add in Main as per normal and a screen will appear. In the middle window click Variables to show the variables you will have either lv = null, da = null or mCustomers = null this will indicate which is null. – MikeT May 30 '18 at 09:33
  • If not using Android Studio and not sure of how to set breakpoint and run in debug mode then add these lines before the line `lv.setAdapter(da);`. The lines are `if (lv == null) { Log.d("ListViewNULL", "Your ListView is Null"); } if (da == null) { Log.d("DataAdapterNULL", "Your DataAdapter is Null"); } if (mCustomers == null) { Log.d("CUSTLISTNULL", "Your CustomerList is Null"); }` Once added run, look at log and you should get at least on one the lines in the log. – MikeT May 30 '18 at 09:41
  • p.s. it could be that you are still using **data** instead of **da** i.e. you should have `da = new dataAdapter(this, mCustomers);` followed by `lv.setAdapter(da);` with the line `final dataAdapter data = new dataAdapter(this, mCustomers);` commented out or removed. – MikeT May 30 '18 at 09:56
  • I checked and all is as it should be, and according to the debugger lv is null. Here is the code it put out lv: null da: dataadapter@5189. So, the listview is the issue. – Potter May 30 '18 at 18:04
  • @Potter the reason, I think, is that that the id of **listView** is defined in **content_main.xml** and therefore is only available in/to the main activity. not in CustomerRcrdActivity (hence `lv = findViewById(R.id.listView);` is null). If you edit layout **activity_customer_rcrd** and add a listview with an **id** and use that **id** to set **lv** it should work. However, I suspect that what you really want is a list, the adapter click listeners etc in the MainActivity and to then invoke the CustomerRcrdActivity only when adding or updating a record. Would that be right? – MikeT May 30 '18 at 19:55
  • Yes, absolutely. I was trying to get the list to display underneath the initial add button on the content_main.xml when the user added an entry, updated, or viewed. By that I mean that after the user input info they would go back to the original screen but the list would show underneath the ADD button if that makes sense. But I can always live with viewing it from the CustomerRcrdActivity if I am going about it wrong. – Potter May 30 '18 at 20:03
  • @Potter if anything yours is the right way (in my opinion). I was going to suggest your way but it's quite a lot of code changing and also the complexity of either having two CustomerRecordActivities (one for Add one for Update) or having one activity that handles both. I'll look at working on this, but with pretty basic layouts. – MikeT May 30 '18 at 20:09
  • My suggesting, get the above working (i.e. add the listview). See how it all works. And then move forward (i.e. hopefully I'll have something ready). – MikeT May 30 '18 at 20:10
  • OK part 1 - Listing on Main, click add will display allow add via customerRcrdActivity, long click (see notes) will do the same. Put this onto [github](https://github.com/Mike-j-t/CarPurchases/tree/master/app). Note disabled action bar stuff for my convenience. – MikeT May 30 '18 at 21:29
  • P.S. I believe that you can tick this question as answered. Perhaps use github (as per link in the previous comment) for further discussions. – MikeT May 30 '18 at 21:34
  • You have been absolutely a miracle! I really appreciate the help. I have gotten everything but update working, which I am sure I can check out and fix accordingly. This has been a great learning experience. I will also check out the github link. – Potter May 30 '18 at 21:55
  • Ok update was my fault was passing dataModel to update so was updating with the original data. Now passing a new Customer build from values set by the getValues() method. Also now long-clicking item in Main correctly populates edit texts (), changing a value activates the update button, so record can be updated. You can also click an item in the list in CustomerRcrdActivity to delete/update. Click clear to activate add (i.e. you can do anything from CustomerRcrdActivity). Changes on github. **NOTE** actionbar stuff has been de-activated. – MikeT May 30 '18 at 22:54
  • Awesome! I will check it out and see if I have done everything correctly. – Potter May 31 '18 at 05:38
0

Potential Issue 1

It's hard to see that anything from your code would immediately stop the application, that is unless CustomerRcrdActivity is started (assuming that it is not the MAIN/first activity), without any user interaction.

In which case it could be that the Activity hasn't been defined as an activity in the mainfest (AndroidManifest.xml).

If not then you should code it as an activity, the following would suffice :-

    <activity android:name=".CustomerRcrdActivity">
    </activity>

The entire manifest would then be something like (shown to give you some idea of where to place the code) :-

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cpa.carpurchases">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".CustomerRcrdActivity">
        </activity>
    </application>
</manifest>

Testing

Copying your code and creating basic layouts, and a customer class and invoking CustomerRcrdActivity from the directly from the main activity. Your code appears to be fine and displays the EditText's and Button's. However, there are then some issues:-

Issue 2 - Clicking on the View button

When the view button is clicked the application will crash as the ListView variable lv has not been initialiased, just declared so it will be null.

You would get an error like :-

05-29 10:09:29.382 1551-1551/cpa.carpurchases E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:389)
        at android.view.View.performClick(View.java:4084)
        at android.view.View$PerformClick.run(View.java:16966)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384)
        at android.view.View.performClick(View.java:4084) 
        at android.view.View$PerformClick.run(View.java:16966) 
        at android.os.Handler.handleCallback(Handler.java:615) 
        at android.os.Handler.dispatchMessage(Handler.java:92) 
        at android.os.Looper.loop(Looper.java:137) 
        at android.app.ActivityThread.main(ActivityThread.java:4745) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:511) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
        at dalvik.system.NativeStart.main(Native Method) 
     Caused by: java.lang.NullPointerException
        at cpa.carpurchases.CustomerRcrdActivity.ShowRecords(CustomerRcrdActivity.java:123)
        at cpa.carpurchases.CustomerRcrdActivity.buttonClicked(CustomerRcrdActivity.java:81)
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:511) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384) 
        at android.view.View.performClick(View.java:4084) 
        at android.view.View$PerformClick.run(View.java:16966) 
        at android.os.Handler.handleCallback(Handler.java:615) 
        at android.os.Handler.dispatchMessage(Handler.java:92) 
        at android.os.Looper.loop(Looper.java:137) 
        at android.app.ActivityThread.main(ActivityThread.java:4745) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:511) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
        at dalvik.system.NativeStart.main(Native Method) 

The fix for this is simple you just need a line, in the onCreate method, something like :-

    lv = (ListView) findViewById(R.id.lv); // For testing I gave the ListView an id of **lv**.

Clicking The View button then works fine (to a fashion at the moment, noting that at present during testing nothing has been added).

Issue 3 - Clicking any of the other buttons

Clicking any other button results (if nothing has been input) results in a NumberFormatException exception when the getValues method is called, as there is nothing in the CarCost field. To circumvent this I simply added :-

    String car_Cost = CarCost.getText().toString(); //<<<<EXISTING CODE
    if (car_Cost.length() < 1) {
        car_Cost = "0.00";
    }
    d_Car_Cost = Double.parseDouble(car_Cost); //<<<< EXISTING CODE

Introducing this fix results in :-

  • the DELETE button working and reporting that the record has been deleted (even though none exists),
  • the UPDATE button results in a null pointer exception (note no data has been input still at this stage)
  • the ADD button fails because there is no entry in the manifest for the Databasehandler class. However, it could not be added as it's not an activity. The upside though, is that it is in fact adding rows to the table (even though no data has been added).

Fix for DELETE button

The deleteCustomer method was changed to return a boolean to indicate if the row had been deleted or not as per :-

public boolean deleteCustomer(int Id)
{
    boolean rv = false;
    SQLiteDatabase dh = this.getWritableDatabase();
    rv = (dh.delete(TABLE_CUSTOMERS, KEY_ID + " = ?",
            new String[] { String.valueOf(Id) }) > 0);
    dh.close();
    return rv;
}

additionally the deleteCustomer was changed to Toast a different message if nothing is deleted as per :-

private void deleteCustomer()
{
    getValues();
     if (dh.deleteCustomer(Id)) {
         Toast.makeText(getApplicationContext(), "Customer Deleted Successfully", Toast.LENGTH_LONG).show();
     } else {
         Toast.makeText(getApplicationContext(), "Customer Not Deleted", Toast.LENGTH_LONG).show();
     }
}

Fix for the ADD Button

The lines that try to start the DatabaseHandler as an Activity were commented out as per :-

        case R.id.button_CR_add:
            // CALL addCustomer() TO ADD TO DATABASE
            addCustomer();

            // START NEW ACTIVITY WHEN ADD BUTTON CLICKED
            //Intent intent = new Intent(this, DatabaseHandler.class);
            //startActivity(intent);

            break;

Additionally so that rows aren't added if nothing has been input I changed the addCustomer method to be :-

// INSERT DATA INTO DATABASE
private void addCustomer()
{
    boolean ok_to_add = true;
    getValues();
    if (F_Name == null || F_Name.length() < 1) ok_to_add = false;
    if (L_Name == null || L_Name.length() < 1) ok_to_add = false;
    if (Car_Make == null || Car_Make.length() < 1) ok_to_add = false;
    if (Car_Model == null || Car_Model.length() < 1) ok_to_add = false;
    if (ok_to_add) {
        dh.addCustomers(new Customer(F_Name, L_Name, Car_Make, Car_Model, d_Car_Cost));
        Toast.makeText(getApplicationContext(), "Customer Added Successfully", Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(getApplicationContext(), "Unable To Add - Some Data hasn't been given", Toast.LENGTH_LONG).show();
    }
}

Data can now be added.

The View button also lists the data.

The Update Button

The error is due to the variable dataModel not being set unless an entry in the list is clicked.

To circumvent this the following code changes were used :-

The updateCustomer method was changed to return an integer (number of update rows) :-

/**
 *UPDATING SINGLE CUSTOMER
 **/
public int updateCustomer(Customer customer, int id)
{
    if (customer == null) {
        return 0;
    }
    SQLiteDatabase dh = this.getWritableDatabase();

    ContentValues values = new ContentValues();
    values.put(KEY_FNAME, customer.getFName());
    values.put(KEY_LNAME, customer.getLName());
    values.put(KEY_CAR_MAKE, customer.get_CarMake());
    values.put(KEY_CAR_MODEL, customer.get_CarModel());
    values.put(KEY_CAR_COST, customer.get_CarCost());

    // UPDATING ROW
    return dh.update(TABLE_CUSTOMERS, values, KEY_ID + " = ?",
            new String[] { String.valueOf(id) });
}

and the updateCustomer method of the CustomerRcrdActivity was changed to Toast a different message if no update was done (always at present) to be :-

private void updateCustomer()
{
    getValues();
     if (dh.updateCustomer(dataModel, Id)>0) {
         Toast.makeText(getApplicationContext(), "Customer Updated Successfully", Toast.LENGTH_LONG).show();
     } else {
         Toast.makeText(getApplicationContext(), "Customer Not Updated", Toast.LENGTH_LONG).show();
     }
MikeT
  • 51,415
  • 16
  • 49
  • 68
  • Thank you so much for taking the time to explain things to me! Now, the app is still stopping before starting and I will post the Logcat log above. – Potter May 29 '18 at 17:45
  • @Potter Have a look at the answer to this question[This Activity already has an action bar supplied by the window decor](https://stackoverflow.com/questions/26515058/this-activity-already-has-an-action-bar-supplied-by-the-window-decor). It's your MainActivity that is crashing due to asking for an ActionBar when it already has one. – MikeT May 29 '18 at 19:58