Android 添加更多保存结果到记事本示例
我试图通过操作 Google 已经通过示例编写的代码来学习 Android 平台。我一直在编辑记事本示例,并向注释本身添加了一些新项目,现在正在尝试允许其保存。直到它尝试将值输入数据库进行保存之前,它似乎没有问题,但作为新手,我很难确定位置。下面我决定从 NoteEditor.java 文件复制我的代码:
public class NoteEditor extends Activity {
private static final String TAG = "NoteEditor";
/**
* Standard projection for the interesting columns of a normal note.
*/
private static final String[] PROJECTION = new String[] {
NoteColumns._ID, // 0
NoteColumns.NOTE, // 1
NoteColumns.TITLE, // 2
//NoteColumns.DATE, //3
//NoteColumns.TIME, //4
//NoteColumns.CHOICE, //5
};
/** The index of the note column */
private static final int COLUMN_INDEX_NOTE = 1;
/** The index of the title column */
private static final int COLUMN_INDEX_TITLE = 2;
// This is our state data that is stored when freezing.
private static final String ORIGINAL_CONTENT = "origContent";
// The different distinct states the activity can be run in.
private static final int STATE_EDIT = 0;
private static final int STATE_INSERT = 1;
private int mState;
private Uri mUri;
private Cursor mCursor;
private DatePicker mPicker;
private TimePicker mTime;
private Spinner mChoice;
private EditText mText;
private String mOriginalContent;
/**
* A custom EditText that draws lines between each line of text that is displayed.
*/
public static class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x800000FF);
}
@Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, r);
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
super.onDraw(canvas);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
// Do some setup based on the action being performed.
final String action = intent.getAction();
if (Intent.ACTION_EDIT.equals(action)) {
// Requested to edit: set that state, and the data being edited.
mState = STATE_EDIT;
mUri = intent.getData();
} else if (Intent.ACTION_INSERT.equals(action)) {
// Requested to insert: set that state, and create a new entry
// in the container.
mState = STATE_INSERT;
mUri = getContentResolver().insert(intent.getData(), null);
// If we were unable to create a new note, then just finish
// this activity. A RESULT_CANCELED will be sent back to the
// original activity if they requested a result.
if (mUri == null) {
Log.e(TAG, "Failed to insert new project into " + getIntent().getData());
finish();
return;
}
// The new entry was created, so assume all will end well and
// set the result to be returned.
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
} else {
// Whoops, unknown action! Bail.
Log.e(TAG, "Unknown action, exiting");
finish();
return;
}
// Set the layout for this activity. You can find it in res/layout/note_editor.xml
setContentView(R.layout.note_editor);
Spinner spinner = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.spinnerss, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
// The text view for our note, identified by its ID in the XML file.
mText = (EditText) findViewById(R.id.note);
mTime = (TimePicker) findViewById(R.id.timePicker1);
mPicker = (DatePicker) findViewById(R.id.datePicker1);
mChoice = (Spinner) findViewById(R.id.spinner1);
// Get the note!
mCursor = managedQuery(mUri, PROJECTION, null, null, null);
// If an instance of this activity had previously stopped, we can
// get the original text it started with.
if (savedInstanceState != null) {
mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
}
}
@Override
protected void onResume() {
super.onResume();
// If we didn't have any trouble retrieving the data, it is now
// time to get at the stuff.
if (mCursor != null) {
// Requery in case something changed while paused (such as the title)
mCursor.requery();
// Make sure we are at the one and only row in the cursor.
mCursor.moveToFirst();
// Modify our overall title depending on the mode we are running in.
if (mState == STATE_EDIT) {
// Set the title of the Activity to include the note title
String title = mCursor.getString(COLUMN_INDEX_TITLE);
Resources res = getResources();
String text = String.format(res.getString(R.string.title_edit), title);
setTitle(text);
} else if (mState == STATE_INSERT) {
setTitle(getText(R.string.title_create));
}
// This is a little tricky: we may be resumed after previously being
// paused/stopped. We want to put the new text in the text view,
// but leave the user where they were (retain the cursor position
// etc). This version of setText does that for us.
String note = mCursor.getString(COLUMN_INDEX_NOTE);
mText.setTextKeepState(note);
// If we hadn't previously retrieved the original text, do so
// now. This allows the user to revert their changes.
if (mOriginalContent == null) {
mOriginalContent = note;
}
} else {
setTitle(getText(R.string.error_title));
mText.setText(getText(R.string.error_message));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Save away the original text, so we still have it if the activity
// needs to be killed while paused.
outState.putString(ORIGINAL_CONTENT, mOriginalContent);
}
@Override
protected void onPause() {
super.onPause();
// The user is going somewhere, so make sure changes are saved
String text = mText.getText().toString();
int length = text.length();
// If this activity is finished, and there is no text, then we
// simply delete the note entry.
// Note that we do this both for editing and inserting... it
// would be reasonable to only do it when inserting.
if (isFinishing() && (length == 0) && mCursor != null) {
setResult(RESULT_CANCELED);
deleteNote();
} else {
saveNote();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate menu from XML resource
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.editor_options_menu, menu);
// Append to the
// menu items for any other activities that can do stuff with it
// as well. This does a query on the system for any activities that
// implement the ALTERNATIVE_ACTION for our data, adding a menu item
// for each one that is found.
Intent intent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
new ComponentName(this, NoteEditor.class), null, intent, 0, null);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (mState == STATE_EDIT) {
menu.setGroupVisible(R.id.menu_group_edit, true);
menu.setGroupVisible(R.id.menu_group_insert, false);
// Check if note has changed and enable/disable the revert option
String savedNote = mCursor.getString(COLUMN_INDEX_NOTE);
String currentNote = mText.getText().toString();
if (savedNote.equals(currentNote)) {
menu.findItem(R.id.menu_revert).setEnabled(false);
} else {
menu.findItem(R.id.menu_revert).setEnabled(true);
}
} else {
menu.setGroupVisible(R.id.menu_group_edit, false);
menu.setGroupVisible(R.id.menu_group_insert, true);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle all of the possible menu actions.
switch (item.getItemId()) {
case R.id.menu_save:
saveNote();
finish();
break;
case R.id.menu_delete:
deleteNote();
finish();
break;
case R.id.menu_revert:
case R.id.menu_discard:
cancelNote();
break;
}
return super.onOptionsItemSelected(item);
}
private final void saveNote() {
// Make sure their current
// changes are safely saved away in the provider. We don't need
// to do this if only editing.
if (mCursor != null) {
// Get out updates into the provider.
ContentValues values = new ContentValues();
// Bump the modification time to now.
values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
String text = mText.getText().toString();
//String date = mPicker.toString();
//String time = mTime.toString();
String choice = mChoice.toString();
int length = text.length();
// If we are creating a new note, then we want to also create
// an initial title for it.
if (mState == STATE_INSERT) {
if (length == 0) {
Toast.makeText(this, R.string.nothing_to_save, Toast.LENGTH_SHORT).show();
return;
}
String title = text.substring(0, Math.min(30, length));
if (length > 30) {
int lastSpace = title.lastIndexOf(' ');
if (lastSpace > 0) {
title = title.substring(0, lastSpace);
}
}
values.put(NoteColumns.TITLE, title);
}
// Write our text back into the provider.
values.put(NoteColumns.NOTE, text);
//values.put(NoteColumns.DATE, date);
//values.put(NoteColumns.TIME, time);
//values.put(NoteColumns.CHOICE, choice);
// Commit all of our changes to persistent storage. When the update completes
// the content provider will notify the cursor of the change, which will
// cause the UI to be updated.
try {
getContentResolver().update(mUri, values, null, null);
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
}
}
}
/**
* Take care of canceling work on a note. Deletes the note if we
* had created it, otherwise reverts to the original text.
*/
private final void cancelNote() {
if (mCursor != null) {
if (mState == STATE_EDIT) {
// Put the original note text back into the database
mCursor.close();
mCursor = null;
ContentValues values = new ContentValues();
values.put(NoteColumns.NOTE, mOriginalContent);
getContentResolver().update(mUri, values, null, null);
} else if (mState == STATE_INSERT) {
// We inserted an empty note, make sure to delete it
deleteNote();
}
}
setResult(RESULT_CANCELED);
finish();
}
/**
* Take care of deleting a note. Simply deletes the entry.
*/
private final void deleteNote() {
if (mCursor != null) {
mCursor.close();
mCursor = null;
getContentResolver().delete(mUri, null, null);
mText.setText("");
}
}
}
希望我没有从文件中包含太多内容,我删除了顶部的导入和版权以节省更多空间,但不确定需要什么来正确分析问题并帮助我更好地解释它。
谢谢 迈克尔
DDMS:
07-18 10:27:49.757: ERROR/Database(25136): Error updating choice=android.widget.Spinner@40551ea0 note=Blah burger
07-18 10:27:49.757: ERROR/Database(25136): T
07-18 10:27:49.757: ERROR/Database(25136): modified=1311002869768 using UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): FATAL EXCEPTION: main
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): java.lang.RuntimeException: Unable to pause activity {com.example.android.notepad/com.example.android.notepad.NoteEditor}: android.database.sqlite.SQLiteException: no such column: choice: , while compiling: UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2731)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2678)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:2651)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.access$1700(ActivityThread.java:132)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1049)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.os.Handler.dispatchMessage(Handler.java:99)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.os.Looper.loop(Looper.java:143)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.main(ActivityThread.java:4263)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at java.lang.reflect.Method.invokeNative(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at java.lang.reflect.Method.invoke(Method.java:507)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at dalvik.system.NativeStart.main(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): Caused by: android.database.sqlite.SQLiteException: no such column: choice: , while compiling: UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:92)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:65)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:83)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:41)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1231)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1813)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1763)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NotePadProvider.update(NotePadProvider.java:235)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.content.ContentProvider$Transport.update(ContentProvider.java:240)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.content.ContentResolver.update(ContentResolver.java:726)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NoteEditor.saveNote(NoteEditor.java:357)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NoteEditor.onPause(NoteEditor.java:247)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.Activity.performPause(Activity.java:3988)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1313)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2708)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): ... 12 more
NotepadProvider.java:
public class NotePadProvider extends ContentProvider {
private static final String TAG = "NotePadProvider";
private static final String DATABASE_NAME = "notepad.db";
private static final int DATABASE_VERSION = 2;
private static final String NOTES_TABLE_NAME = "notes";
private static HashMap<String, String> sNotesProjectionMap;
private static HashMap<String, String> sLiveFolderProjectionMap;
private static final int NOTES = 1;
private static final int NOTE_ID = 2;
private static final int LIVE_FOLDER_NOTES = 3;
private static final UriMatcher sUriMatcher;
/**
* This class helps open, create, and upgrade the database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
+ NoteColumns._ID + " INTEGER PRIMARY KEY,"
+ NoteColumns.TITLE + " TEXT,"
+ NoteColumns.NOTE + " TEXT,"
+ NoteColumns.TIME + " TEXT,"
+ NoteColumns.DATE + " TEXT,"
+ NoteColumns.CHOICE + " TEXT,"
+ NoteColumns.CREATED_DATE + " INTEGER,"
+ NoteColumns.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
private DatabaseHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(NOTES_TABLE_NAME);
switch (sUriMatcher.match(uri)) {
case NOTES:
qb.setProjectionMap(sNotesProjectionMap);
break;
case NOTE_ID:
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(NoteColumns._ID + "=" + uri.getPathSegments().get(1));
break;
case LIVE_FOLDER_NOTES:
qb.setProjectionMap(sLiveFolderProjectionMap);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = NoteColumns.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case NOTES:
case LIVE_FOLDER_NOTES:
return NoteColumns.CONTENT_TYPE;
case NOTE_ID:
return NoteColumns.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
// Validate the requested uri
if (sUriMatcher.match(uri) != NOTES) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(NoteColumns.CREATED_DATE) == false) {
values.put(NoteColumns.CREATED_DATE, now);
}
if (values.containsKey(NoteColumns.MODIFIED_DATE) == false) {
values.put(NoteColumns.MODIFIED_DATE, now);
}
if (values.containsKey(NoteColumns.TITLE) == false) {
Resources r = Resources.getSystem();
values.put(NoteColumns.TITLE, r.getString(android.R.string.untitled));
}
if (values.containsKey(NoteColumns.NOTE) == false) {
values.put(NoteColumns.NOTE, "");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(NOTES_TABLE_NAME, NoteColumns.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(NoteColumns.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(NOTES_TABLE_NAME, NoteColumns._ID + "=" + noteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(NOTES_TABLE_NAME, values, NoteColumns._ID + "=" + noteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "choice", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "date", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "time", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES);
sNotesProjectionMap = new HashMap<String, String>();
sNotesProjectionMap.put(NoteColumns._ID, NoteColumns._ID);
sNotesProjectionMap.put(NoteColumns.TITLE, NoteColumns.TITLE);
sNotesProjectionMap.put(NoteColumns.NOTE, NoteColumns.NOTE);
sNotesProjectionMap.put(NoteColumns.CHOICE, NoteColumns.CHOICE);
sNotesProjectionMap.put(NoteColumns.TIME, NoteColumns.TIME);
sNotesProjectionMap.put(NoteColumns.DATE, NoteColumns.DATE);
sNotesProjectionMap.put(NoteColumns.CREATED_DATE, NoteColumns.CREATED_DATE);
sNotesProjectionMap.put(NoteColumns.MODIFIED_DATE, NoteColumns.MODIFIED_DATE);
// Support for Live Folders.
sLiveFolderProjectionMap = new HashMap<String, String>();
sLiveFolderProjectionMap.put(LiveFolders._ID, NoteColumns._ID + " AS " +
LiveFolders._ID);
sLiveFolderProjectionMap.put(LiveFolders.NAME, NoteColumns.TITLE + " AS " +
LiveFolders.NAME);
// Add more columns here for more robust Live Folders.
}
}
Im trying to learn the android platform by manipulating code already written by Google through the samples. I have been editing the Notepad sample, and have added a few new items to the note itself, and am now trying to allow it to save. It doesnt seem to have a problem until it tries to enter the values into the db for saving it seems, but being a newb, Im having a hard time pin pointing the location. Below I have decided to copy my code from the NoteEditor.java file:
public class NoteEditor extends Activity {
private static final String TAG = "NoteEditor";
/**
* Standard projection for the interesting columns of a normal note.
*/
private static final String[] PROJECTION = new String[] {
NoteColumns._ID, // 0
NoteColumns.NOTE, // 1
NoteColumns.TITLE, // 2
//NoteColumns.DATE, //3
//NoteColumns.TIME, //4
//NoteColumns.CHOICE, //5
};
/** The index of the note column */
private static final int COLUMN_INDEX_NOTE = 1;
/** The index of the title column */
private static final int COLUMN_INDEX_TITLE = 2;
// This is our state data that is stored when freezing.
private static final String ORIGINAL_CONTENT = "origContent";
// The different distinct states the activity can be run in.
private static final int STATE_EDIT = 0;
private static final int STATE_INSERT = 1;
private int mState;
private Uri mUri;
private Cursor mCursor;
private DatePicker mPicker;
private TimePicker mTime;
private Spinner mChoice;
private EditText mText;
private String mOriginalContent;
/**
* A custom EditText that draws lines between each line of text that is displayed.
*/
public static class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x800000FF);
}
@Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, r);
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
super.onDraw(canvas);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
// Do some setup based on the action being performed.
final String action = intent.getAction();
if (Intent.ACTION_EDIT.equals(action)) {
// Requested to edit: set that state, and the data being edited.
mState = STATE_EDIT;
mUri = intent.getData();
} else if (Intent.ACTION_INSERT.equals(action)) {
// Requested to insert: set that state, and create a new entry
// in the container.
mState = STATE_INSERT;
mUri = getContentResolver().insert(intent.getData(), null);
// If we were unable to create a new note, then just finish
// this activity. A RESULT_CANCELED will be sent back to the
// original activity if they requested a result.
if (mUri == null) {
Log.e(TAG, "Failed to insert new project into " + getIntent().getData());
finish();
return;
}
// The new entry was created, so assume all will end well and
// set the result to be returned.
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
} else {
// Whoops, unknown action! Bail.
Log.e(TAG, "Unknown action, exiting");
finish();
return;
}
// Set the layout for this activity. You can find it in res/layout/note_editor.xml
setContentView(R.layout.note_editor);
Spinner spinner = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.spinnerss, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
// The text view for our note, identified by its ID in the XML file.
mText = (EditText) findViewById(R.id.note);
mTime = (TimePicker) findViewById(R.id.timePicker1);
mPicker = (DatePicker) findViewById(R.id.datePicker1);
mChoice = (Spinner) findViewById(R.id.spinner1);
// Get the note!
mCursor = managedQuery(mUri, PROJECTION, null, null, null);
// If an instance of this activity had previously stopped, we can
// get the original text it started with.
if (savedInstanceState != null) {
mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
}
}
@Override
protected void onResume() {
super.onResume();
// If we didn't have any trouble retrieving the data, it is now
// time to get at the stuff.
if (mCursor != null) {
// Requery in case something changed while paused (such as the title)
mCursor.requery();
// Make sure we are at the one and only row in the cursor.
mCursor.moveToFirst();
// Modify our overall title depending on the mode we are running in.
if (mState == STATE_EDIT) {
// Set the title of the Activity to include the note title
String title = mCursor.getString(COLUMN_INDEX_TITLE);
Resources res = getResources();
String text = String.format(res.getString(R.string.title_edit), title);
setTitle(text);
} else if (mState == STATE_INSERT) {
setTitle(getText(R.string.title_create));
}
// This is a little tricky: we may be resumed after previously being
// paused/stopped. We want to put the new text in the text view,
// but leave the user where they were (retain the cursor position
// etc). This version of setText does that for us.
String note = mCursor.getString(COLUMN_INDEX_NOTE);
mText.setTextKeepState(note);
// If we hadn't previously retrieved the original text, do so
// now. This allows the user to revert their changes.
if (mOriginalContent == null) {
mOriginalContent = note;
}
} else {
setTitle(getText(R.string.error_title));
mText.setText(getText(R.string.error_message));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Save away the original text, so we still have it if the activity
// needs to be killed while paused.
outState.putString(ORIGINAL_CONTENT, mOriginalContent);
}
@Override
protected void onPause() {
super.onPause();
// The user is going somewhere, so make sure changes are saved
String text = mText.getText().toString();
int length = text.length();
// If this activity is finished, and there is no text, then we
// simply delete the note entry.
// Note that we do this both for editing and inserting... it
// would be reasonable to only do it when inserting.
if (isFinishing() && (length == 0) && mCursor != null) {
setResult(RESULT_CANCELED);
deleteNote();
} else {
saveNote();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate menu from XML resource
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.editor_options_menu, menu);
// Append to the
// menu items for any other activities that can do stuff with it
// as well. This does a query on the system for any activities that
// implement the ALTERNATIVE_ACTION for our data, adding a menu item
// for each one that is found.
Intent intent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
new ComponentName(this, NoteEditor.class), null, intent, 0, null);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (mState == STATE_EDIT) {
menu.setGroupVisible(R.id.menu_group_edit, true);
menu.setGroupVisible(R.id.menu_group_insert, false);
// Check if note has changed and enable/disable the revert option
String savedNote = mCursor.getString(COLUMN_INDEX_NOTE);
String currentNote = mText.getText().toString();
if (savedNote.equals(currentNote)) {
menu.findItem(R.id.menu_revert).setEnabled(false);
} else {
menu.findItem(R.id.menu_revert).setEnabled(true);
}
} else {
menu.setGroupVisible(R.id.menu_group_edit, false);
menu.setGroupVisible(R.id.menu_group_insert, true);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle all of the possible menu actions.
switch (item.getItemId()) {
case R.id.menu_save:
saveNote();
finish();
break;
case R.id.menu_delete:
deleteNote();
finish();
break;
case R.id.menu_revert:
case R.id.menu_discard:
cancelNote();
break;
}
return super.onOptionsItemSelected(item);
}
private final void saveNote() {
// Make sure their current
// changes are safely saved away in the provider. We don't need
// to do this if only editing.
if (mCursor != null) {
// Get out updates into the provider.
ContentValues values = new ContentValues();
// Bump the modification time to now.
values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
String text = mText.getText().toString();
//String date = mPicker.toString();
//String time = mTime.toString();
String choice = mChoice.toString();
int length = text.length();
// If we are creating a new note, then we want to also create
// an initial title for it.
if (mState == STATE_INSERT) {
if (length == 0) {
Toast.makeText(this, R.string.nothing_to_save, Toast.LENGTH_SHORT).show();
return;
}
String title = text.substring(0, Math.min(30, length));
if (length > 30) {
int lastSpace = title.lastIndexOf(' ');
if (lastSpace > 0) {
title = title.substring(0, lastSpace);
}
}
values.put(NoteColumns.TITLE, title);
}
// Write our text back into the provider.
values.put(NoteColumns.NOTE, text);
//values.put(NoteColumns.DATE, date);
//values.put(NoteColumns.TIME, time);
//values.put(NoteColumns.CHOICE, choice);
// Commit all of our changes to persistent storage. When the update completes
// the content provider will notify the cursor of the change, which will
// cause the UI to be updated.
try {
getContentResolver().update(mUri, values, null, null);
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
}
}
}
/**
* Take care of canceling work on a note. Deletes the note if we
* had created it, otherwise reverts to the original text.
*/
private final void cancelNote() {
if (mCursor != null) {
if (mState == STATE_EDIT) {
// Put the original note text back into the database
mCursor.close();
mCursor = null;
ContentValues values = new ContentValues();
values.put(NoteColumns.NOTE, mOriginalContent);
getContentResolver().update(mUri, values, null, null);
} else if (mState == STATE_INSERT) {
// We inserted an empty note, make sure to delete it
deleteNote();
}
}
setResult(RESULT_CANCELED);
finish();
}
/**
* Take care of deleting a note. Simply deletes the entry.
*/
private final void deleteNote() {
if (mCursor != null) {
mCursor.close();
mCursor = null;
getContentResolver().delete(mUri, null, null);
mText.setText("");
}
}
}
Hopefully I didnt include too much from the file, I knocked out the imports and copyrights at the top to save more room, but wasnt sure what was needed to properly analyze the issue and help explain it better to me.
Thanks
Michael
DDMS:
07-18 10:27:49.757: ERROR/Database(25136): Error updating choice=android.widget.Spinner@40551ea0 note=Blah burger
07-18 10:27:49.757: ERROR/Database(25136): T
07-18 10:27:49.757: ERROR/Database(25136): modified=1311002869768 using UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): FATAL EXCEPTION: main
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): java.lang.RuntimeException: Unable to pause activity {com.example.android.notepad/com.example.android.notepad.NoteEditor}: android.database.sqlite.SQLiteException: no such column: choice: , while compiling: UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2731)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2678)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:2651)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.access$1700(ActivityThread.java:132)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1049)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.os.Handler.dispatchMessage(Handler.java:99)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.os.Looper.loop(Looper.java:143)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.main(ActivityThread.java:4263)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at java.lang.reflect.Method.invokeNative(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at java.lang.reflect.Method.invoke(Method.java:507)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at dalvik.system.NativeStart.main(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): Caused by: android.database.sqlite.SQLiteException: no such column: choice: , while compiling: UPDATE notes SET choice=?, note=?, modified=? WHERE _id=1
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:92)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:65)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:83)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:41)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1231)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1813)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1763)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NotePadProvider.update(NotePadProvider.java:235)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.content.ContentProvider$Transport.update(ContentProvider.java:240)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.content.ContentResolver.update(ContentResolver.java:726)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NoteEditor.saveNote(NoteEditor.java:357)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at com.example.android.notepad.NoteEditor.onPause(NoteEditor.java:247)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.Activity.performPause(Activity.java:3988)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1313)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2708)
07-18 10:27:49.817: ERROR/AndroidRuntime(25136): ... 12 more
NotepadProvider.java:
public class NotePadProvider extends ContentProvider {
private static final String TAG = "NotePadProvider";
private static final String DATABASE_NAME = "notepad.db";
private static final int DATABASE_VERSION = 2;
private static final String NOTES_TABLE_NAME = "notes";
private static HashMap<String, String> sNotesProjectionMap;
private static HashMap<String, String> sLiveFolderProjectionMap;
private static final int NOTES = 1;
private static final int NOTE_ID = 2;
private static final int LIVE_FOLDER_NOTES = 3;
private static final UriMatcher sUriMatcher;
/**
* This class helps open, create, and upgrade the database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
+ NoteColumns._ID + " INTEGER PRIMARY KEY,"
+ NoteColumns.TITLE + " TEXT,"
+ NoteColumns.NOTE + " TEXT,"
+ NoteColumns.TIME + " TEXT,"
+ NoteColumns.DATE + " TEXT,"
+ NoteColumns.CHOICE + " TEXT,"
+ NoteColumns.CREATED_DATE + " INTEGER,"
+ NoteColumns.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
private DatabaseHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(NOTES_TABLE_NAME);
switch (sUriMatcher.match(uri)) {
case NOTES:
qb.setProjectionMap(sNotesProjectionMap);
break;
case NOTE_ID:
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(NoteColumns._ID + "=" + uri.getPathSegments().get(1));
break;
case LIVE_FOLDER_NOTES:
qb.setProjectionMap(sLiveFolderProjectionMap);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = NoteColumns.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case NOTES:
case LIVE_FOLDER_NOTES:
return NoteColumns.CONTENT_TYPE;
case NOTE_ID:
return NoteColumns.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
// Validate the requested uri
if (sUriMatcher.match(uri) != NOTES) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(NoteColumns.CREATED_DATE) == false) {
values.put(NoteColumns.CREATED_DATE, now);
}
if (values.containsKey(NoteColumns.MODIFIED_DATE) == false) {
values.put(NoteColumns.MODIFIED_DATE, now);
}
if (values.containsKey(NoteColumns.TITLE) == false) {
Resources r = Resources.getSystem();
values.put(NoteColumns.TITLE, r.getString(android.R.string.untitled));
}
if (values.containsKey(NoteColumns.NOTE) == false) {
values.put(NoteColumns.NOTE, "");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(NOTES_TABLE_NAME, NoteColumns.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(NoteColumns.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(NOTES_TABLE_NAME, NoteColumns._ID + "=" + noteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
break;
case NOTE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(NOTES_TABLE_NAME, values, NoteColumns._ID + "=" + noteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "choice", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "date", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "time", NOTES);
sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES);
sNotesProjectionMap = new HashMap<String, String>();
sNotesProjectionMap.put(NoteColumns._ID, NoteColumns._ID);
sNotesProjectionMap.put(NoteColumns.TITLE, NoteColumns.TITLE);
sNotesProjectionMap.put(NoteColumns.NOTE, NoteColumns.NOTE);
sNotesProjectionMap.put(NoteColumns.CHOICE, NoteColumns.CHOICE);
sNotesProjectionMap.put(NoteColumns.TIME, NoteColumns.TIME);
sNotesProjectionMap.put(NoteColumns.DATE, NoteColumns.DATE);
sNotesProjectionMap.put(NoteColumns.CREATED_DATE, NoteColumns.CREATED_DATE);
sNotesProjectionMap.put(NoteColumns.MODIFIED_DATE, NoteColumns.MODIFIED_DATE);
// Support for Live Folders.
sLiveFolderProjectionMap = new HashMap<String, String>();
sLiveFolderProjectionMap.put(LiveFolders._ID, NoteColumns._ID + " AS " +
LiveFolders._ID);
sLiveFolderProjectionMap.put(LiveFolders.NAME, NoteColumns.TITLE + " AS " +
LiveFolders.NAME);
// Add more columns here for more robust Live Folders.
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
DDMS 输出显示在尝试保存时出现错误:
显然您的数据库中没有 CHOICE 列,或者您有一个但已将其删除。
此错误位于
com.example.android.notepad.NotePadProvider
类的update
方法中。您没有发布该类的源代码,但应该很容易发现问题。The DDMS output shows that there was an error as it tried to do the save:
Apparently you either didn't have a CHOICE column in your database, or you had one but have since removed it.
This error is in the
update
method of yourcom.example.android.notepad.NotePadProvider
class. You didn't post the source for that class, but it should be pretty easy to spot the problem.好的,谢谢您发布该信息。粗略地(双关语)一瞥告诉我数据库没有名为“CHOICE”的列。乍一看,您的数据库创建代码看起来不错。
也许您的系统上挂着旧版本的数据库。擦除您的用户数据以清除所有旧数据库。
OK thanks for posting that. A cursory (pun intended) glance tells me that the database does not have a column called "CHOICE". Your database creation code looks OK at a glance.
Maybe an old version of the database is hanging out on your system. Wipe your user data to clear out any old databases.