I'm new to coding so I apologise if this is really obvious, I've searched for the exception and either get completely lost/understand the issue but don't know how to fix it.
The below is the code for an Inventory app, the below is the activity for a user to enter info of an item to add to a database. I believe the issue occurs in either the 'saveItemCheck' method. The saveItemCheck method should be ensuring the user has entered info into the required fields and if not display a toast message, however the app stops and the logcat details;
java.lang.NumberFormatException: empty String
at java.lang.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1071)
at java.lang.Float.parseFloat(Float.java:459)
at com.example.android.inventoryapp.EditorActivity.saveItemCheck(EditorActivity.java:97)
So, from this, I know the issue is that it doesn't like the Float and probably won't like the int if the code got that far but I don't know how to fix it. Again, I apologize for my lack of knowledge, I guess this is going to be really obvious but my brain has gone after working on this project for the last two weeks.
I've included the whole file for context and in case I'm wrong and I'm missing something else.
public class EditorActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int EXISTING_INVENTORY_LOADER = 0;
/** Content URI for the existing item (null if it's a new item) */
private Uri mCurrentInventoryUri;
/** EditText field to enter the item's name */
private EditText mNameEditText;
/** EditText field to enter the item's price */
private EditText mPriceEditText;
/** EditText field to enter the quantity of the item */
private EditText mQuantityEditText;
/** EditText field to enter the item's supplier */
private EditText mSupplierEditText;
/** EditText field to enter the supplier's number */
private EditText mSupplierNumberEditText;
private boolean mItemHasChanged = false;
private View.OnTouchListener mTouchListener =new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent){
mItemHasChanged = true;
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_editor);
Intent intent = getIntent();
mCurrentInventoryUri = intent.getData();
if (mCurrentInventoryUri == null) {
setTitle(getString(R.string.editor_activity_title_new_item));
invalidateOptionsMenu();
}else{
setTitle(getString(R.string.editor_activity_update_an_item));
getLoaderManager().initLoader(EXISTING_INVENTORY_LOADER, null, this);
}
// Find all relevant views that we will need to read user input from
mNameEditText = (EditText) findViewById(R.id.edit_item_name);
mPriceEditText = (EditText) findViewById(R.id.edit_item_price);
mQuantityEditText = (EditText) findViewById(R.id.edit_item_quantity);
mSupplierEditText = (EditText) findViewById(R.id.edit_item_supplier_name);
mSupplierNumberEditText = (EditText) findViewById(R.id.edit_item_supplier_number);
mNameEditText.setOnTouchListener(mTouchListener);
mPriceEditText.setOnTouchListener(mTouchListener);
mQuantityEditText.setOnTouchListener(mTouchListener);
mSupplierEditText.setOnTouchListener(mTouchListener);
mSupplierNumberEditText.setOnTouchListener(mTouchListener);
}
private void saveItemCheck() {
String nameString = mNameEditText.getText().toString().trim();
String priceString = mPriceEditText.getText().toString().trim();
Float price = Float.parseFloat(priceString);
String quantityString = mQuantityEditText.getText().toString().trim();
int quantity = Integer.parseInt(quantityString);
String supplierString = mSupplierEditText.getText().toString().trim();
String supplierNumberString = mSupplierNumberEditText.getText().toString().trim();
if (mCurrentInventoryUri == null &&
TextUtils.isEmpty(nameString) && TextUtils.isEmpty(priceString) &&
TextUtils.isEmpty(quantityString) && TextUtils.isEmpty(supplierString) &&
TextUtils.isEmpty(supplierNumberString)) {
return;
}
if (TextUtils.isEmpty(nameString) || TextUtils.isEmpty(priceString)
|| TextUtils.isEmpty(quantityString) || TextUtils.isEmpty(supplierString)
|| TextUtils.isEmpty(supplierNumberString)) {
Toast.makeText(this, "Please fill all the starred fields to save the product",
Toast.LENGTH_LONG).show();
} else {
saveItem(nameString, price, quantity, supplierString, supplierNumberString);
}
}
private void saveItem(String nameString, Float price, int quantity, String supplierString, String supplierNumberString) {
ContentValues values = new ContentValues();
values.put(InventoryEntry.COLUMN_ITEM_NAME, nameString);
values.put(InventoryEntry.COLUMN_ITEM_PRICE, price);
values.put(InventoryEntry.COLUMN_ITEM_QUANTITY, quantity);
values.put(InventoryEntry.COLUMN_ITEM_SUPPLIER, supplierString);
values.put(InventoryEntry.COLUMN_ITEM_NUMBER, supplierNumberString);
if (mCurrentInventoryUri == null) {
// This is a NEW item, so insert a new item into the provider,
// returning the content URI for the new item.
Uri newUri = getContentResolver().insert(InventoryEntry.CONTENT_URI, values);
// Show a toast message depending on whether or not the insertion was successful.
if (newUri == null) {
// If the new content URI is null, then there was an error with insertion.
Toast.makeText(this, getString(R.string.item_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the insertion was successful and we can display a toast.
Toast.makeText(this, getString(R.string.item_added),
Toast.LENGTH_SHORT).show();
}
} else {
// Otherwise this is an EXISTING item, so update the item with content URI: mCurrentInventoryUri
// and pass in the new ContentValues. Pass in null for the selection and selection args
// because mCurrentInventoryUri will already identify the correct row in the database that
// we want to modify.
int rowsAffected = getContentResolver().update(mCurrentInventoryUri, values, null, null);
// Show a toast message depending on whether or not the update was successful.
if (rowsAffected == 0) {
// If no rows were affected, then there was an error with the update.
Toast.makeText(this, getString(R.string.item_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the update was successful and we can display a toast.
Toast.makeText(this, getString(R.string.item_added),
Toast.LENGTH_SHORT).show();
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_editor.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_editor, menu);
return true;
}
/**
* This method is called after invalidateOptionsMenu(), so that the
* menu can be updated (some menu items can be hidden or made visible).
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
// If this is a new item, hide the "Delete" menu item.
if (mCurrentInventoryUri == null) {
MenuItem menuItem = menu.findItem(R.id.action_delete);
menuItem.setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Save" menu option
case R.id.action_save:
// Save item to database
saveItemCheck();
// Exit activity
finish();
return true;
// Respond to a click on the "Delete" menu option
case R.id.action_delete:
deleteItem();
return true;
// Respond to a click on the "Up" arrow button in the app bar
case android.R.id.home:
// If the item hasn't changed, continue with navigating up to parent activity
// which is the {@link CatalogActivity}.
if (!mItemHasChanged) {
NavUtils.navigateUpFromSameTask(EditorActivity.this);
return true;
}
// Otherwise if there are unsaved changes, setup a dialog to warn the user.
// Create a click listener to handle the user confirming that
// changes should be discarded.
DialogInterface.OnClickListener discardButtonClickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// User clicked "Discard" button, navigate to parent activity.
NavUtils.navigateUpFromSameTask(EditorActivity.this);
}
};
// Show a dialog that notifies the user they have unsaved changes
showUnsavedChangesDialog(discardButtonClickListener);
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* This method is called when the back button is pressed.
*/
@Override
public void onBackPressed() {
// If the item hasn't changed, continue with handling back button press
if (!mItemHasChanged) {
super.onBackPressed();
return;
}
// Otherwise if there are unsaved changes, setup a dialog to warn the user.
// Create a click listener to handle the user confirming that changes should be discarded.
DialogInterface.OnClickListener discardButtonClickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// User clicked "Discard" button, close the current activity.
finish();
}
};
// Show dialog that there are unsaved changes
showUnsavedChangesDialog(discardButtonClickListener);
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
// Since the editor shows all pet attributes, define a projection that contains
// all columns from the Stock table
String[] projection = {
InventoryEntry._ID,
InventoryEntry.COLUMN_ITEM_NAME,
InventoryEntry.COLUMN_ITEM_PRICE,
InventoryEntry.COLUMN_ITEM_QUANTITY,
InventoryEntry.COLUMN_ITEM_SUPPLIER,
InventoryEntry.COLUMN_ITEM_NUMBER};
// This loader will execute the ContentProvider's query method on a background thread
return new CursorLoader(this, // Parent activity context
mCurrentInventoryUri, // Query the content URI for the current item
projection, // Columns to include in the resulting Cursor
null, // No selection clause
null, // No selection arguments
null); // Default sort order
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Bail early if the cursor is null or there is less than 1 row in the cursor
if (cursor == null || cursor.getCount() < 1) {
return;
}
// Proceed with moving to the first row of the cursor and reading data from it
// (This should be the only row in the cursor)
if (cursor.moveToFirst()) {
// Find the columns of pet attributes that we're interested in
int nameColumnIndex = cursor.getColumnIndex(InventoryEntry.COLUMN_ITEM_NAME);
int priceColumnIndex = cursor.getColumnIndex(InventoryEntry.COLUMN_ITEM_PRICE);
int quantityColumnIndex = cursor.getColumnIndex(InventoryEntry.COLUMN_ITEM_QUANTITY);
int supplierColumnIndex = cursor.getColumnIndex(InventoryEntry.COLUMN_ITEM_SUPPLIER);
int supplierNumberColumnIndex = cursor.getColumnIndex(InventoryEntry.COLUMN_ITEM_NUMBER);
// Extract out the value from the Cursor for the given column index
String name = cursor.getString(nameColumnIndex);
Float price = cursor.getFloat(priceColumnIndex);
int quantity = cursor.getInt(quantityColumnIndex);
String supplier = cursor.getString(supplierColumnIndex);
String supplierNumber = cursor.getString(supplierNumberColumnIndex);
// Update the views on the screen with the values from the database
mNameEditText.setText(name);
mPriceEditText.setText(Float.toString(price));
mQuantityEditText.setText(Integer.toString(quantity));
mSupplierEditText.setText(supplier);
mSupplierNumberEditText.setText(supplierNumber);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// If the loader is invalidated, clear out all the data from the input fields.
mNameEditText.setText("");
mPriceEditText.setText("");
mQuantityEditText.setText("");
mSupplierEditText.setText("");
mSupplierNumberEditText.setText("");
}
/**
* Show a dialog that warns the user there are unsaved changes that will be lost
* if they continue leaving the editor.
*
* @param discardButtonClickListener is the click listener for what to do when
* the user confirms they want to discard their changes
*/
private void showUnsavedChangesDialog(
DialogInterface.OnClickListener discardButtonClickListener) {
// Create an AlertDialog.Builder and set the message, and click listeners
// for the positive and negative buttons on the dialog.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.unsaved_changes_dialog_msg);
builder.setPositiveButton(R.string.discard, discardButtonClickListener);
builder.setNegativeButton(R.string.keep_editing, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User clicked the "Keep editing" button, so dismiss the dialog
// and continue editing the item.
if (dialog != null) {
dialog.dismiss();
}
}
});
// Create and show the AlertDialog
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
/**
* Perform the deletion of the pet in the database.
*/
private void deleteItem() {
// Only perform the delete if this is an existing item.
if (mCurrentInventoryUri != null) {
// Call the ContentResolver to delete the item at the given content URI.
// Pass in null for the selection and selection args because the mCurrentPetUri
// content URI already identifies the item that we want.
int rowsDeleted = getContentResolver().delete(mCurrentInventoryUri, null, null);
// Show a toast message depending on whether or not the delete was successful.
if (rowsDeleted == 0) {
// If no rows were deleted, then there was an error with the delete.
Toast.makeText(this, getString(R.string.editor_delete_item_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the delete was successful and we can display a toast.
Toast.makeText(this, getString(R.string.editor_delete_item_successful),
Toast.LENGTH_SHORT).show();
}
}
// Close the activity
finish();
}
}
This means that you tried to cast an empty String to a float number.
Maybe here: Float price = Float.parseFloat(priceString);
So rewrite as:
private void saveItemCheck() {
String nameString = mNameEditText.getText().toString().trim();
String priceString = mPriceEditText.getText().toString().trim();
String quantityString = mQuantityEditText.getText().toString().trim();
String supplierString = mSupplierEditText.getText().toString().trim();
String supplierNumberString = mSupplierNumberEditText.getText().toString().trim();
if (mCurrentInventoryUri == null &&
TextUtils.isEmpty(nameString) && TextUtils.isEmpty(priceString) &&
TextUtils.isEmpty(quantityString) && TextUtils.isEmpty(supplierString) &&
TextUtils.isEmpty(supplierNumberString)) {
return;
}
if (TextUtils.isEmpty(nameString) || TextUtils.isEmpty(priceString)
|| TextUtils.isEmpty(quantityString) || TextUtils.isEmpty(supplierString)
|| TextUtils.isEmpty(supplierNumberString)) {
Toast.makeText(this, "Please fill all the starred fields to save the product",
Toast.LENGTH_LONG).show();
} else {
Float price = Float.parseFloat(priceString);
int quantity = Integer.parseInt(quantityString);
saveItem(nameString, price, quantity, supplierString, supplierNumberString);
}
}