java - Android Studio 数据库列必须 > 0 错误
问题描述
我正在为一门大学课程做作业。我对编码真的很陌生,所以如果这是一个明显的解决方法,我深表歉意。任务是:
创建一个包含两个表单的活动应用程序。主窗体应该有一个列表视图、一个添加按钮、一个编辑按钮、一个查看按钮和一个删除按钮。单击任何按钮时,您应该将用户导航到第二个表单,该表单将使用使用意图对象传递的数据为按钮操作正确填充。您的第二个表单应该是输入姓名和地址的布局。该表格需要包含名字、姓氏、街道地址、城镇、州和邮政编码。您的所有输入字段都应正确标记/提示,以便用户知道要输入什么。除了这些字段之外,您还需要一个“确定”、“取消”和“清除”按钮。如果用户单击“清除”按钮,您需要清除所有条目。如果用户单击“确定”或“取消”按钮,您应该使用意图对象将按钮选择和表单数据返回到主表单。主要活动应处理响应并添加、更新或删除记录。应在应用程序自动创建的 SQLite 数据库中添加、更新或删除所有记录。您的程序应生成一条警报消息,确认操作已完成。
我在 cursorToAddressAttributeGroup 方法中收到一个错误,指出值 cursor.getColumnIndex 应该大于 0。如何将其设置为大于 0?这是有问题的班级:
package com.example.program5;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.*;
import com.example.program5.AddressCollection.AddressCollectionDB;
public class AddressDatabaseHelper extends SQLiteOpenHelper
{
public static final String TABLE_ADDRESS = "address";
public static final String COLUMN_ID = "id";
public static final String COLUMN_FIRSTNAME = "firstName";
public static final String COLUMN_LASTNAME ="lastName";
public static final String COLUMN_STREET_ADDRESS = "streetAddress";
public static final String COLUMN_TOWN = "town";
public static final String COLUMN_STATE = "state";
public static final String COLUMN_ZIP ="zip";
private static final String DATABASE_NAME = "address.db";
private static final int DATABASE_VERSION = 1;
// Database creation SQL statement
private static final String DATABASE_CREATE_SQL = "create table "
+ TABLE_ADDRESS + "("
+ COLUMN_ID + " integer primary key autoincrement, "
+COLUMN_FIRSTNAME + " text not null, "
+COLUMN_LASTNAME + "text not null, "
+COLUMN_STREET_ADDRESS + "text not null, "
+COLUMN_TOWN + "text not null, "
+COLUMN_STATE + "text not null, "
+COLUMN_ZIP + "text not null);";
public AddressDatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//onCreate method creates table
@Override
public void onCreate(SQLiteDatabase database)
{
//directly executes SQl statement create table
database.execSQL(DATABASE_CREATE_SQL);
}
//onUpgrade method upgrades the table
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
//drops table if it exists
db.execSQL("DROP TABLE IF EXISTS " + TABLE_ADDRESS);
//calls onCreate method and passes db as an argument
onCreate(db);
}
}
class AddressDataSource
{
private SQLiteDatabase database;
private final AddressDatabaseHelper dbHelper;
private String[] allColumns = { AddressDatabaseHelper.COLUMN_ID,
AddressDatabaseHelper.COLUMN_FIRSTNAME, AddressDatabaseHelper.COLUMN_LASTNAME,
AddressDatabaseHelper.COLUMN_STREET_ADDRESS, AddressDatabaseHelper.COLUMN_TOWN,
AddressDatabaseHelper.COLUMN_STATE, AddressDatabaseHelper.COLUMN_ZIP };
//AddressDataSource passes c as argument
public AddressDataSource(Context c)
{
super();
// dbHelper equals AddressDatabaseHelper passing c as parameter
dbHelper = new AddressDatabaseHelper(c);
}
//open method
public void open() throws SQLException {
//sets database equal to dbHelper.getWritableDatabase
database = dbHelper.getWritableDatabase();
}
//close method
public void close()
{
//closes dbHelper
dbHelper.close();
}
//createAddress method passes address as argument
public long createAddress(AddressAttributeGroup address)
{
//creates new ContentValues object called values
ContentValues values = new ContentValues();
//puts values of firstname and lastname into COLUMN_NAME
values.put(AddressDatabaseHelper.COLUMN_ID,address.id);
values.put(AddressDatabaseHelper.COLUMN_FIRSTNAME, address.firstName );
values.put(AddressDatabaseHelper.COLUMN_LASTNAME, address.lastName );
values.put(AddressDatabaseHelper.COLUMN_STREET_ADDRESS, address.streetAddress);
values.put(AddressDatabaseHelper.COLUMN_TOWN,
address.town);
values.put(AddressDatabaseHelper.COLUMN_STATE,address.state);
values.put(AddressDatabaseHelper.COLUMN_ZIP,address.zip);
/* returns insertId */
long insertId = database.insert(AddressDatabaseHelper.TABLE_ADDRESS,
null,
values);
return insertId;
// return database.insert(AddressDatabaseHelper.TABLE_ADDRESS,null,values);
}
//deleteAddress method passes address as argument
public void deleteAddress(AddressAttributeGroup address)
{ //sets id equal to address.id
long id = address.id;
//deletes the entry stored at address.id in the TABLE_ADDRESS table
database.delete(AddressDatabaseHelper.TABLE_ADDRESS,
AddressDatabaseHelper.COLUMN_ID + " = " + id, null);
}
//getAllAddresses method
public AddressCollectionDB getAllAddresses() throws Exception
{ AddressCollectionDB addresses = new AddressCollectionDB();
Cursor cursor = database.query(AddressDatabaseHelper.TABLE_ADDRESS,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast())
{
//adds address to addresses
addresses.addAddress(cursorToAddressAttributeGroup(cursor));
//and moves cursor to next entry
cursor.moveToNext();
}
//closes the cursor
cursor.close();
//returns addresses
return addresses;
}
private AddressAttributeGroup cursorToAddressAttributeGroup(Cursor cursor)
{
//error must be greater than 0. Figure out what is causing the error?
(cursor.getInt(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_ID)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_FIRSTNAME)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_LASTNAME)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_STREET_ADDRESS)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_TOWN)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_STATE)),
cursor.getString(cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_ZIP)));
}
}
以下是上下文程序中的其他类:
地址属性组类:
package com.example.program5;
public class AddressAttributeGroup {
//declare Strings for name and address and a long for the id number
public long id;
public String firstName;
public String lastName;
public String streetAddress;
public String town;
public String state;
public String zip;
//constructor
public AddressAttributeGroup(long id, String firstName, String lastName, String streetAddress,
String town, String state, String zip) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.streetAddress = streetAddress;
this.town = town;
this.state = state;
this.zip = zip;
}
地址集合类:
package com.example.program5;
import android.content.Context;
import java.util.ArrayList;
public class AddressCollection {
ArrayList<AddressAttributeGroup> addressList = new ArrayList<>();
final int MAXIMUM_ADDRESS_COUNT = 10;
//boolean that checks if amount of records in arrayList equal to or greater than limit
public boolean isAddressLimitReached()
{
return (addressList.size() >= MAXIMUM_ADDRESS_COUNT);
}
public int addAddress(AddressAttributeGroup address) throws Exception {
//if arrayList is full
if (isAddressLimitReached())
{
throw (new Exception("Maximum Address Reached."));
}
//adds new address to the arraylist addressList
addressList.add(address);
//returns the index of address
return addressList.indexOf(address);
}
public void setAddress(int addressIndex, AddressAttributeGroup address) {
addressList.set(addressIndex, address);
}
public void removeAddress(int addressIndex) {
//removes address at the index addressIndex
addressList.remove(addressIndex);
}
//
public AddressAttributeGroup getAddress(int addressIndex) {
return addressList.get(addressIndex);
}
static class AddressCollectionDB extends AddressCollection
{static public AddressDataSource addressData;
public static AddressCollectionDB AddressCollectionFactory(Context context)
{
AddressCollectionDB resultCode = new AddressCollectionDB();
addressData = new AddressDataSource(context);
try {
addressData.open();
resultCode = addressData.getAllAddresses();
addressData.close();
} catch (Exception e) {
e.printStackTrace();
}
return resultCode;
}
public int addAddress (AddressAttributeGroup address) throws Exception
{
if (isAddressLimitReached())
throw (new Exception("Maximum Address Reached."));
addressData.open();
address.id = addressData.createAddress(address);
addressData.close();
return super.addAddress(address);
}
public void setAddress ( int addressIndex, AddressAttributeGroup address)
{
addressData.open();
addressData.deleteAddress(getAddress(addressIndex));
address.id = addressData.createAddress(address);
addressData.close();
super.setAddress(addressIndex, address);
}
public void removeAddress ( int addressIndex)
{
addressData.open();
addressData.deleteAddress(getAddress(addressIndex));
addressData.close();
super.removeAddress(addressIndex);
}
}
}
地址控制台类:
package com.example.program5;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.program5.StrongIntent.TypeOfAction;
import com.example.program5.AddressCollection.AddressCollectionDB;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class AddressConsole extends AppCompatActivity implements View.OnClickListener,
AdapterView.OnItemClickListener, AdapterView.OnItemSelectedListener {
Button cmdAdd;
Button cmdDelete;
Button cmdView;
Button cmdEdit;
TextView textAddressMessage;
EditText editRecordNumber;
ListView listOfAddresses;
final int ADDRESS_ENTRY = 1001;
AddressCollectionDB addresses;
//AddressCollection addresses = new AddressCollection();
AddressArrayAdapter addressAdapter;
int recordNumber = +1;
@Override
protected void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
setContentView(R.layout.address_console);
textAddressMessage = findViewById(R.id.textAddressMessage);
editRecordNumber = findViewById(R.id.editRecordNumber);
cmdAdd = findViewById(R.id.cmdAdd);
cmdAdd.setOnClickListener(this);
cmdEdit = findViewById(R.id.cmdEdit);
cmdEdit.setOnClickListener(this);
cmdDelete = findViewById(R.id.cmdDelete);
cmdDelete.setOnClickListener(this);
cmdView = findViewById(R.id.cmdView);
cmdView.setOnClickListener(this);
listOfAddresses = findViewById(R.id.listOfAddresses);
listOfAddresses.setOnItemClickListener (this);
listOfAddresses.setOnItemSelectedListener( this);
addresses = AddressCollectionDB.AddressCollectionFactory(this);
addressAdapter = new AddressArrayAdapter(this, R.layout.row_layout, addresses);
listOfAddresses.setAdapter(addressAdapter);
}
void displayError(Exception message) {
Toast.makeText(this,message.getMessage(),Toast.LENGTH_LONG).show();
}
void displayError(){
Toast.makeText(this, "Maximum Number Of Addresses Reached!",Toast.LENGTH_LONG).show();
}
void displayInfo(String message)
{
Toast.makeText(this, message,Toast.LENGTH_SHORT).show();
}
//hmm why is this never used?
void displayAddressMessage(AddressAttributeGroup address)
{//issue
textAddressMessage.setText("Address:" + address.firstName + " " + address.lastName + " " +
address.streetAddress + " "
+ address.town + "" + address.state + " " + address.zip);
}
void clearAddressMessage()
{
textAddressMessage.setText("");
}
@Override
public void onClick(View view) {
StrongIntent intent;
//if user clicks add button
if(cmdAdd.getId() == view.getId())
{
//if address limit has not been reached
if(!addresses.isAddressLimitReached())
{
//intent equals new Strong Intent
intent = new StrongIntent();
//intent action is equal to ADD
intent.action = TypeOfAction.ADD;
startActivityForResult(intent.getIntent(this, AddressEntry.class),ADDRESS_ENTRY);
}
//else
else
//calls display error message
displayError();
}
else
{
try {
{
AddressAttributeGroup address = addresses.getAddress(recordNumber);
//if user clicks edit
if(cmdEdit.getId() == view.getId())
{
//intent equals new StrongIntent passing address, typeOfAction and record number as arguments
intent = new StrongIntent(address,TypeOfAction.EDIT,recordNumber);
startActivityForResult(intent.getIntent(this,AddressEntry.class),ADDRESS_ENTRY);
}
//if user clicks delete button
if(cmdDelete.getId()==view.getId())
{
//intent equals StrongIntent passing address DELETE and record number as arguments
intent = new StrongIntent(address, TypeOfAction.DELETE,recordNumber);
startActivityForResult(intent.getIntent(this,AddressEntry.class),ADDRESS_ENTRY);
}
//if user clicks view
if(cmdView.getId() == view.getId())
{
//calls displayAddressMessage and passes address as an argument
displayAddressMessage(address);
}
}
//catch block
} catch (Exception ex) {
displayError(ex);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
StrongIntent addressIntent = new StrongIntent(data);
if (requestCode == ADDRESS_ENTRY) {
try {
//switch statement passes resultCode as argument
switch (resultCode) {
case RESULT_OK:
AddressAttributeGroup address = new AddressAttributeGroup(addressIntent.addressIndex,addressIntent.firstName, addressIntent.lastName,
addressIntent.streetAddress, addressIntent.town, addressIntent.state, addressIntent.zip);
switch (addressIntent.action) {
case ADD:
addresses.addAddress(address);
displayAddressMessage(address);
displayInfo("Added + Address:" + address.firstName + " " + address.lastName + " " + address.streetAddress + " "
+ address.town + "" + address.state + " " + address.zip);
break;
case DELETE:
addresses.removeAddress(addressIntent.addressIndex);
displayInfo("Address Deleted");
clearAddressMessage();
editRecordNumber.setText("");
recordNumber =-1;
break;
case EDIT:
addresses.setAddress(addressIntent.addressIndex,address);
displayAddressMessage(address);
displayInfo("Address Updated");
break;
}
addressAdapter.notifyDataSetChanged();
break;
case RESULT_CANCELED:
displayInfo("Cancelled");
break;
}
}
//catch block
catch(Exception ex)
{
displayError(ex);
}
}
super.onActivityResult(requestCode,resultCode,data);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onItemSelected(parent, view, position,id);
AddressAttributeGroup address = addresses.getAddress(recordNumber);
displayAddressMessage(address);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
recordNumber = position;
editRecordNumber.setText(String.valueOf(recordNumber));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
class AddressArrayAdapter extends ArrayAdapter<AddressAttributeGroup> {
private final Context context;
private final AddressCollection addresses;
public AddressArrayAdapter(Context context, int resource, AddressCollection addresses) {
super(context, resource, addresses.addressList);
this.context = context;
this.addresses = addresses;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
AddressAttributeGroup address = addresses.getAddress(position);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView;
rowView = inflater.inflate(R.layout.row_layout, parent, false);
TextView firstNameTextView = rowView.findViewById(R.id.editFirstName);
TextView lastNameTextView = rowView.findViewById(R.id.editLastName);
TextView addressTextView = rowView.findViewById(R.id.editStreetAddress);
TextView townTextView;
townTextView = rowView.findViewById(R.id.editTown);
TextView stateTextView = rowView.findViewById(R.id.editState);
TextView zipTextView = rowView.findViewById(R.id.editZip);
firstNameTextView.setText(address.firstName);
lastNameTextView.setText(address.lastName);
addressTextView.setText(address.streetAddress);
townTextView.setText(address.town);
stateTextView.setText(address.state);
zipTextView.setText(address.zip);
return rowView;
}
}
}
强意图类:
package com.example.program5;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class StrongIntent
{
public String firstName;
public String lastName;
public String streetAddress;
public String town;
public String state;
public String zip;
public enum TypeOfAction
{
ADD,
EDIT,
DELETE
}
TypeOfAction action;
int addressIndex = +1;
Intent intent;
public StrongIntent(Intent intent)
{
Bundle bundle = intent.getExtras();
//try/catch block for retrieving key value pairs for intent bundle
try
{
firstName = bundle.getString("firstName");
lastName = bundle.getString("lastName");
streetAddress = bundle.getString("streetAddress");
town = bundle.getString("town");
state = bundle.getString("state");
zip = bundle.getString("zip");
//retrieves the action type chosen
action = TypeOfAction.values()[bundle.getInt("action",0)];
//gets address index
addressIndex = bundle.getInt("addressIndex");
}
//catch block
catch (Exception ex) {
ex.printStackTrace();
}
}
//constructor
public StrongIntent()
{ firstName = "";
lastName = "";
streetAddress = "";
town = "";
state= "";
zip = "";
}
//constructor which passes addressAttributes, action and addressIndex as arguments
public StrongIntent(AddressAttributeGroup addressAttributes, TypeOfAction action, int addressIndex)
{
firstName = addressAttributes.firstName;
lastName = addressAttributes.lastName;
streetAddress = addressAttributes.streetAddress;
town = addressAttributes.town;
state= addressAttributes.state;
zip = addressAttributes.zip;
this.action = action;
this.addressIndex = addressIndex;
}
//sets intent equal to null
public void clearIntent()
{
intent = null;
}
//adds data to intent bundle
void putExtras()
{
intent.putExtra("firstName",firstName);
intent.putExtra("lastName",lastName);
intent.putExtra("streetAddress",streetAddress);
intent.putExtra("town",town);
intent.putExtra("state",state);
intent.putExtra("zip",zip);
intent.putExtra("action",action.ordinal());
intent.putExtra("addressIndex",addressIndex);
}
public Intent getIntent()
{ //if intent is equal to null
if (intent == null)
//sets intent equal to new Intent
{ intent = new Intent();
//calls putExtras method
putExtras();
}
//returns intent
return intent;
}
public Intent getIntent(Activity addressEntry,
Class<AddressEntry> class1)
{
//if intent is equal to null
if (intent == null)
{
intent = new Intent(addressEntry,class1);
//calls putExtras method
putExtras();
}
//returns intent
return intent;
}
}
地址入口类:
package com.example.program5;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class AddressEntry extends Activity implements View.OnClickListener{
Button cmdSave;
Button cmdClear;
Button cmdCancel;
EditText editFirstName;
EditText editLastName;
EditText editStreetAddress;
EditText editTown;
EditText editState;
EditText editZip;
int result;
StrongIntent stIntent;
@Override
public void onCreate(Bundle savedInstanceState)
{ super.onCreate(savedInstanceState);
setContentView(R.layout.address_entry);
editFirstName = findViewById(R.id.editFirstName);
editLastName = findViewById(R.id.editLastName);
editStreetAddress = findViewById(R.id.editStreetAddress) ;
editTown = findViewById(R.id.editTown);
editState = findViewById(R.id.editState);
editZip = findViewById(R.id.editZip);
cmdSave = findViewById(R.id.cmdSave);
cmdSave.setOnClickListener(this);
cmdClear = findViewById(R.id.cmdClear);
cmdClear.setOnClickListener(this);
cmdCancel = findViewById(R.id.cmdCancel);
cmdCancel.setOnClickListener(this);
stIntent = new StrongIntent(getIntent());
editFirstName.setText(stIntent.firstName);
editLastName.setText(stIntent.lastName);
editStreetAddress.setText(stIntent.streetAddress);
editTown.setText(stIntent.town);
editState.setText(stIntent.state);
editZip.setText(stIntent.zip);
//if the type of action is equal to DELETE
if (stIntent.action ==StrongIntent.TypeOfAction.DELETE)
//if user clicks Save sets text to Delete
cmdSave.setText(R.string.deleteString);
//enables action when delete is not selected
editFirstName.setEnabled(stIntent.action!=StrongIntent.TypeOfAction.DELETE);
editLastName.setEnabled(stIntent.action!=StrongIntent.TypeOfAction.DELETE);
editStreetAddress.setEnabled(stIntent.action != StrongIntent.TypeOfAction.DELETE);
editTown.setEnabled(stIntent.action != StrongIntent.TypeOfAction.DELETE);
editState.setEnabled(stIntent.action != StrongIntent.TypeOfAction.DELETE);
editZip.setEnabled(stIntent.action != StrongIntent.TypeOfAction.DELETE);
cmdClear.setEnabled(stIntent.action!=StrongIntent.TypeOfAction.DELETE);
}
@Override
public void onClick(View view)
{
//if Save is clicked
if(cmdSave.getId() == view.getId())
{
//result equals RESULT_OK
result = RESULT_OK;
//calls the finish method
finish();
}
//if Clear is clicked
if(cmdClear.getId() == view.getId())
//clears all values entered
{ editFirstName.setText("");
editLastName.setText("");
editStreetAddress.setText("");
editTown.setText("");
editState.setText("");
editZip.setText("");
}//if Cancel is clicked
if(cmdCancel.getId() == view.getId())
{ //result is equal to RESULT_CANCELED
result = RESULT_CANCELED;
//calls finish method
finish();
}
}
@Override
public void finish()
{ //clears intent
stIntent.clearIntent();
//sets firstName equal to editFirstName
stIntent.firstName= editFirstName.getText().toString();
//sets lastName equal to editLastName
stIntent.lastName = editLastName.getText().toString();
//sets streetAddress equal to editStreetAddress
stIntent.streetAddress =editStreetAddress.getText().toString();
//sets town equal to editTown
stIntent.town = editTown.getText().toString();
//sets state equal to editState
stIntent.state = editState.getText().toString();
//sets zip equal to editZip
stIntent.zip = editZip.getText().toString();
//sets result passing result and the intent as arguments
setResult(result, stIntent.getIntent());
//calls super.finish method
super.finish();
}
}
解决方案
我认为原因是 SDK 31 的问题。
规避此问题的选项是执行以下操作之一:-
- 更改为在构建 gradle 中使用 SDK 30。
- 使用
@SuppressLint("Range")
前就行private AddressAttributeGroup cursorToAddressAttributeGroup(Cursor cursor)
- 使用
getColumnIndexOrThrow
代替getColumnIndex
- 在获取之前获取索引????
例如
int idx = cursor.getColumnIndex(AddressDatabaseHelper.COLUMN_ID);
cursor.getInt(idx);
您还应该更改:
// Database creation SQL statement
private static final String DATABASE_CREATE_SQL = "create table "
+ TABLE_ADDRESS + "("
+ COLUMN_ID + " integer primary key autoincrement, "
+COLUMN_FIRSTNAME + " text not null, "
+COLUMN_LASTNAME + "text not null, "
+COLUMN_STREET_ADDRESS + "text not null, "
+COLUMN_TOWN + "text not null, "
+COLUMN_STATE + "text not null, "
+COLUMN_ZIP + "text not null);";
至 :-
// Database creation SQL statement
private static final String DATABASE_CREATE_SQL = "create table "
+ TABLE_ADDRESS + "("
+ COLUMN_ID + " integer primary key autoincrement, "
+COLUMN_FIRSTNAME + " text not null, "
+COLUMN_LASTNAME + " text not null, "
+COLUMN_STREET_ADDRESS + "text not null, " //<<<<< ADDED SPACE
+COLUMN_TOWN + " text not null, " //<<<<< ADDED SPACE
+COLUMN_STATE + " text not null, " //<<<<< ADDED SPACE
+COLUMN_ZIP + " text not null);"; //<<<<< ADDED SPACE
否则,列名(用 注释的//<<<<< ADDED SPACE
)将是预期的以文本为后缀的列名,然后将导致返回 -1 并在运行时产生列索引错误。例如,列名不是姓氏,而是姓氏文本