问题
I am trying to get timestamp to show- I have tried the onCreate
query in different ways and also tried to to have addTime
as a value in addPrime
. Nothing seems to work. My intention is for the app to show previous primes and the time that they were found. The intention for the app is for the user to be able to close/kill the app and resume counting from last found prime number when restarting the app, if you have any hints how also this would be possible I would be grateful.
This is the PrimeDBManager class
public class PrimeDBManager extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "prime.db";
public static final String TABLE_PRIME = "prime";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_PRIMENO = "primeno";
public static final String COLUMN_DATETIME = "datetime";
public PrimeDBManager(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE " + TABLE_PRIME + "(" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_PRIMENO + " TEXT " + COLUMN_DATETIME + " DATETIME DEFAULT CURRENT_TIMESTAMP " + ");";
db.execSQL(query);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRIME);
onCreate(db);
}
//Add a new prime to the database
public void addPrime(Prime prime){
ContentValues values = new ContentValues();
values.put(COLUMN_PRIMENO, prime.get_primeno());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_PRIME, null, values);
}
public void addTime(Prime prime) {
ContentValues values = new ContentValues();
values.put(COLUMN_DATETIME, prime.get_datetime());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_PRIME, null, values);
}
public String databaseToString(){
String dbString = "";
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_PRIME + " WHERE 1";
Cursor c = db.rawQuery(query, null);
c.moveToFirst();
while(!c.isAfterLast()) {
if (c.getString(c.getColumnIndex("primeno"))!=null){
dbString += c.getString(c.getColumnIndex("primeno"));
dbString += "\n";
}
c.moveToNext();
}
db.close();
return dbString;
}
}
Prime class
public class Prime {
private int _id;
private String _primeno;
private String _datetime;
public Prime(){ }
public Prime(String _primeno) {
this._primeno = _primeno;
}
public void set_id(int _id) {
this._id = _id;
}
public void set_primeno(String _primeno) {
this._primeno = _primeno;
}
public int get_id() {
return _id;
}
public String get_primeno() {
return _primeno;
}
public void set_datetime(String _datetime) {
this._datetime = _datetime;
}
public String get_datetime() {
return _datetime;
}
}
And lastly the MainActivity class
public class MainActivity extends ActionBarActivity {
Button primeButton;
int max = 500;
TextView primeText;
int j = 2;
TextView previousPrime;
PrimeDBManager dbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
primeButton = (Button) findViewById(R.id.primeButton);
primeText = (TextView) findViewById(R.id.primeText);
previousPrime = (TextView) findViewById(R.id.previousPrime);
dbManager = new PrimeDBManager(this, null, null, 1);
printDatabase();
primeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
for (int i = j; i <= max; i++) {
if (isPrimeNumber(i)) {
primeText.setText(i+"");
j = i+1;
break;
}
}
Prime prime = new Prime(primeText.getText().toString());
dbManager.addPrime(prime);
dbManager.addTime(prime);
printDatabase();
}
});
}
public void printDatabase () {
String dbString = dbManager.databaseToString();
previousPrime.setText(dbString);
}
public boolean isPrimeNumber(int number) {
for (int i = 2; i <= number / 2; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
}
回答1:
Ok, since you uploaded your project I think I got it working the way you want. It is working nonetheless.
There were several errors - mostly with logic. I tried to comment as much as I could so you can understand what/why I was doing everything.
One thing I did not comment was that the AndroidManifest
needed permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
You can download the project here, or just look at the snippets:
MainActivity
I added a ListView
so you can see all the primes. Also changed how/where we get the data from the DB, and how we save to DB.
public class MainActivity extends ActionBarActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private int max = 500;
private TextView primeText;
private int previousPrimeNumber;
private List<Prime> primes;
private PrimeAdapter adapter;
private MyDBHandler dbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
primeText = (TextView) findViewById(R.id.primeText);
//get the object from previous session. Remember these are sorted by date
dbManager = new MyDBHandler(this);
primes = dbManager.getPrimeObjects();
//get the first prime. (AKA the last one added)
if (primes.size() != 0) {
previousPrimeNumber = primes.get(0).get_primeno(); //get the first item
primeText.setText(String.valueOf(previousPrimeNumber));
} else {
previousPrimeNumber = 2;
}
//create list view and adapter to display the data
ListView listView = (ListView) findViewById(R.id.listView);
adapter = new PrimeAdapter(this, primes);
listView.setAdapter(adapter);
findViewById(R.id.primeButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int primeNumber = -1;
//increment previousPrimeNumber by one so we wont keep using previousPrimeNumber
for (int i = previousPrimeNumber + 1; i <= max; i++) {
if (isPrimeNumber(i)) {
primeNumber = i;
primeText.setText(String.valueOf(i));
previousPrimeNumber = i + 1;
break;
}
}
if (primeNumber != -1) {
Prime prime = new Prime(primeNumber);
dbManager.addPrime(prime);
/* Yes, it saved to our database. But there is no reason for us to read from
* it too when we have the prime object right here. So just add it to the
* adapter and be done */
//The adapter is looking at the list primes. So add it to the top and notify
primes.add(0, prime);
adapter.notifyDataSetChanged();
} else {
Log.e(TAG, "Oops, there was an error. Invalid prime number");
}
}
});
}
public boolean isPrimeNumber(int number) {
for (int i = 2; i <= number / 2; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
/**
* If this is too confusing you can ignore it for now.
* However, I recommend understanding the android UIs before diving in to database storage.
* Take a look at this link:
* http://www.vogella.com/tutorials/AndroidListView/article.html
*/
private class PrimeAdapter extends ArrayAdapter<Prime> {
public PrimeAdapter(Context context, List<Prime> primes) {
// I am just using androids views. (android.R.id...)
super(context, android.R.layout.simple_list_item_2, primes);
}
@Override
public View getView(int position, View view, ViewGroup parent) {
/* This method will automagically get called for every item in the list.
* This is an ARRAY adapter. So it has a list of the data we passed in on
* the constructor. So by calling "this" we are accessing it like it were a list
* which it really is. */
final Prime prime = this.getItem(position);
if (view == null) {
view = LayoutInflater.from(MainActivity.this)
.inflate(android.R.layout.simple_list_item_2, null);
}
/* if you look at simple_list_item_2, you will see two textViews. text1 and text2.
* Normally you would create this view yourself, but like i said, that is not the
* reason I am here */
// Notice I am referencing android.R.id. and not R.id. That is cause I am lazy and
// didn't create my own views.
TextView tv1 = (TextView) view.findViewById(android.R.id.text1);
TextView tv2 = (TextView) view.findViewById(android.R.id.text2);
tv1.setText(String.valueOf(prime.get_primeno()));
tv2.setText(prime.getDateTimeFormatted());
//now return the view so the listView knows to show it
return view;
}
}
MyDBHandler:
Changed the queries and added two methods that will convert Prime
to a ContentValues
object and from Cursor
to a prime.
public class MyDBHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "prime.db";
public static final String TABLE_PRIME = "prime";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_PRIMENO = "primeno";
public static final String COLUMN_DATETIME = "datetime";
public MyDBHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE " + TABLE_PRIME + "(" +
/* This must be in same order everywhere! */
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + // ID will be index 0
COLUMN_PRIMENO + " INTEGER, " + // Prime will be index 1
COLUMN_DATETIME + " LONG);"; // Date will be index 2
db.execSQL(query);
/* Something else to note: I changed the column types. You had text for these,
* which is fine. But the object that you are storing in each of these is not
* a string. So for consistency store the object as its original class type:
* PrimeNo == integer
* Datetime == long (milliseconds)
* This also makes it so sorting is much easier */
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRIME);
onCreate(db);
}
/**
* You want to save the entire Prime object at once. Not bits and pieces.
*
* @param prime
*/
public void addPrime(Prime prime) {
ContentValues values = writePrime(prime);
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_PRIME, null, values);
/* DON'T FORGET TO CLOSE YOUR DATABASE! */
db.close();
}
/**
* Again, you want to receive the entire prime object at once. Not bits.
*
* @return List of previous prime objects
*/
public List<Prime> getPrimeObjects() {
final List<Prime> primes = new ArrayList<Prime>();
final SQLiteDatabase db = getWritableDatabase();
/* Normally i would use this line of code:
final Cursor c = db.rawQuery("SELECT * FROM " + TABLE_PRIME, null);
but, you want to be sure you will get them order by DATE so you know
the first prime in the list is the last added. so I switched the query to this:
*/
final Cursor c = db.query(TABLE_PRIME,
new String[]{COLUMN_ID, COLUMN_PRIMENO, COLUMN_DATETIME},
null, null, null, null, //much null. So wow.
COLUMN_DATETIME + " DESC"); //order in descending.
/* After queried the first item will be our starting point */
c.moveToFirst();
while (c.moveToNext()) {
Prime p = buildPrime(c);
//check not null
if (p != null)
primes.add(p); //add to list
}
/* DON'T FORGET TO CLOSE YOUR DATABASE AND CURSOR! */
c.close();
db.close();
return primes;
}
/**
* Convert the Cursor object back in to a prime number
*
* @param cursor Cursor
* @return Prime
*/
private Prime buildPrime(Cursor cursor) {
final Prime prime = new Prime();
prime.set_id(cursor.getInt(0)); // id index as stated above
prime.set_primeno(cursor.getInt(1)); // prime index as stated above
prime.setDateTime(cursor.getLong(2)); // date index as stated above
return prime;
}
/**
* Convert the prime object in to ContentValues to write to DB
*
* @param prime prime
* @return ContentValues
*/
private ContentValues writePrime(Prime prime) {
ContentValues values = new ContentValues();
values.put(COLUMN_PRIMENO, prime.get_primeno()); //must insert first
values.put(COLUMN_DATETIME, prime.getDateTime()); //must insert second
return values;
}
}
Prime:
I just changed the value types. Prime
's purpose is to store a prime integer. So why not make that field a integer?
public class Prime {
private static final String format = "yyyy-MM-dd HH:mm:ss";
private static final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.ENGLISH);
private int _id;
private int _primeno;
private long dateTime = 0; //this is in milliseconds. Makes it easier to manage
public Prime() { }
public Prime(int primeNumber) {
//create a new prime object with a prime already known. set the date while we are at it
this._primeno = primeNumber;
this.dateTime = System.currentTimeMillis();
}
public void set_id(int _id) {
this._id = _id;
}
public void set_primeno(int _primeno) {
this._primeno = _primeno;
}
public int get_id() {
return _id;
}
public int get_primeno() {
return _primeno;
}
public long getDateTime() {
return dateTime;
}
public String getDateTimeFormatted() {
if (dateTime == 0) {
dateTime = System.currentTimeMillis();
}
Date date = new Date(dateTime);
return formatter.format(date);
}
public void setDateTime(long dateTime) {
this.dateTime = dateTime;
}
}
回答2:
This seems to work.
In your values create db.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="prime_table_name">prime</string>
<string name="prime_table_column_id">_id</string>
<string name="prime_table_column_prime_no">prime_no</string>
<string name="prime_table_column_datetime">date_time</string>
<string-array name="prime_table_all_columns">
<item>@string/prime_table_column_id</item>
<item>@string/prime_table_column_prime_no</item>
<item>@string/prime_table_column_datetime</item>
</string-array>
<string name="createTableOfPrimes">CREATE TABLE prime ( _id INTEGER PRIMARY KEY AUTOINCREMENT, prime_no TEXT, date_time DATETIME DEFAULT CURRENT_TIMESTAMP );</string>
<string name="dropTableOfPrimes">DROP TABLE IF EXISTS prime</string>
<string name="insertIntoTableOfPrimes">INSERT INTO prime (prime_no, date_time) VALUES(?,?)</string>
</resources>
Prime class
package si.kseneman.utilities;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
public class Prime {
private static final String format = "yyyy-MM-dd HH:mm:ss";
private static final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.ENGLISH);
private int id;
private String primeNo;
private Calendar dateTime;
public Prime(String primeNo) {
this.id = -1;
this.primeNo = primeNo;
this.dateTime = Calendar.getInstance();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPrimeNo() {
return primeNo;
}
public void setPrimeNo(String primeNo) {
this.primeNo = primeNo;
}
public String getDateTime() {
return formatter.format(dateTime.getTime());
}
public void setDateTime(Calendar calendar) {
this.dateTime = (Calendar) calendar.clone();
}
public void setDateTime(String dateTimeString) {
try {
dateTime.setTime(formatter.parse(dateTimeString));
} catch (ParseException e) {
dateTime.setTimeInMillis(0);
}
}
public void setDateTimeToNow() {
this.dateTime = Calendar.getInstance();
}
}
Prie SQL Lite Helper
package si.kseneman.utilities;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.lang.ref.WeakReference;
import si.kseneman.mobile.R;
public class PrimeDBSQLLiteHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "Prime.db";
private static final String TAG = PrimeDBSQLLiteHelper.class.getSimpleName();
private WeakReference<Context> mContext;
public PrimeDBSQLLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = new WeakReference<Context>(context);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(getString(R.string.createTableOfPrimes));
Log.i(TAG, "DataBase created");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
sqLiteDatabase.execSQL(getString(R.string.dropTableOfPrimes));
this.onCreate(sqLiteDatabase);
}
private String getString(int resID) {
return mContext.get() != null ? mContext.get().getString(resID) : null;
}
}
Prime SingleTon database
package si.kseneman.utilities;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
import android.os.Environment;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import si.kseneman.mobile.R;
public class PrimeDataSource {
private static final String TAG = PrimeDataSource.class.getSimpleName();
private static byte numberOfInstances;
private SQLiteDatabase database;
private PrimeDBSQLLiteHelper primeDBSQLLiteHelper;
private static PrimeDataSource instance;
private static final Object LOCK = new Object();
private Context context;
protected PrimeDataSource(Context context) {
this.context = context.getApplicationContext();
}
public static synchronized PrimeDataSource getInstance(Context context) {
if (instance == null) {
instance = new PrimeDataSource(context);
}
numberOfInstances++;
return instance;
}
public static synchronized void decrementNumberOfInstances() {
if (--numberOfInstances <= 0) {
instance.close();
}
numberOfInstances = numberOfInstances < 0 ? 0 : numberOfInstances; //Sanity?
}
public static synchronized void forceClose() {
numberOfInstances = 0;
instance.close();
}
private void close() {
if (isDatabaseOpenOpen()) {
primeDBSQLLiteHelper.close();
}
primeDBSQLLiteHelper = null;
instance = null;
database = null;
}
public synchronized void open() {
if (database == null || !database.isOpen()) {
primeDBSQLLiteHelper = new PrimeDBSQLLiteHelper(context);
database = primeDBSQLLiteHelper.getWritableDatabase();
}
}
public boolean isDatabaseOpenOpen() {
return database != null && database.isOpen();
}
public synchronized void deleteAllPrimesFromDb() {
try {
database.delete(getString(R.string.prime_table_name), null, null);
} catch (Exception e) {
// Was it really created?
createTableOfPrimes();
}
}
public synchronized void createTableOfPrimes() {
database.execSQL(getString(R.string.createTableOfPrimes));
}
public synchronized void dropTableOfJobs() {
database.execSQL(getString(R.string.dropTableOfPrimes));
}
public synchronized void dropDataBase() {
database.execSQL("DROP DATABASE IF EXISTS " + PrimeDBSQLLiteHelper.DATABASE_NAME);
}
public String getDatabasePath() {
return (Environment.getDataDirectory() + File.separator + PrimeDBSQLLiteHelper.DATABASE_NAME);
}
public synchronized void insertListOfPrimes(List<Prime> data) {
synchronized (LOCK) {
database.beginTransaction();
SQLiteStatement stmt = database.compileStatement(getString(R.string.insertIntoTableOfPrimes));
for (Prime p : data) {
stmt.bindString(1, p.getPrimeNo());
stmt.bindString(2, p.getDateTime());
stmt.executeInsert();
stmt.clearBindings();
}
database.setTransactionSuccessful();
database.endTransaction();
Log.i(TAG, "Insertion success");
}
}
private Prime cursorToPrime(Cursor cursor) {
// ID = 0 ; primeNo = 1; dateTime = 2;
Prime p = new Prime(cursor.getString(1));
p.setId(cursor.getInt(0));
p.setDateTime(cursor.getString(2));
return p;
}
public List<Prime> getListOfPrimes() {
synchronized (LOCK) {
List<Prime> listOfPrimes = new ArrayList<Prime>();
Cursor cursor = database.query(getString(R.string.prime_table_name), getStringArray(R.array.prime_table_all_columns), null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
listOfPrimes.add(cursorToPrime(cursor));
cursor.moveToNext();
}
return listOfPrimes;
}
}
public void insertPrime(Prime prime) {
synchronized (LOCK) {
database.beginTransaction();
SQLiteStatement stmt = database.compileStatement(getString(R.string.insertIntoTableOfPrimes));
stmt.bindString(1, prime.getPrimeNo());
stmt.bindString(2, prime.getDateTime());
stmt.executeInsert();
stmt.clearBindings();
database.setTransactionSuccessful();
database.endTransaction();
}
}
private int getCountFromCursor(Cursor cursor) {
int result = cursor.moveToFirst() ? cursor.getCount() : 0;
cursor.close();
return result;
}
public int getPrimeCount() {
synchronized (LOCK) {
Cursor cursor = database.query(getString(R.string.prime_table_name), new String[]{getString(R.string.prime_table_column_id)}, null, null, null, null, null);
return getCountFromCursor(cursor);
}
}
private String getString(int resID) {
return context.getString(resID);
}
private String[] getStringArray(int resID) {
return context.getResources().getStringArray(resID);
}
private String[] getStringArrayFromResources(int... integers) {
String[] result = new String[integers.length];
for (int i = 0; i < integers.length; ++i) {
result[i] = getString(integers[i]);
}
return result;
}
}
In Activity
private PrimeDataSource dataSource;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataSource = PrimeDataSource.getInstance(getApplicationContext());
dataSource.open();
List<Prime> demoListOfPrimes = new ArrayList<Prime>(5);
demoListOfPrimes.add(new Prime("1"));
demoListOfPrimes.add(new Prime("3"));
demoListOfPrimes.add(new Prime("5"));
demoListOfPrimes.add(new Prime("7"));
demoListOfPrimes.add(new Prime("11"));
dataSource.insertListOfPrimes(demoListOfPrimes);
demoListOfPrimes = dataSource.getListOfPrimes();
if(demoListOfPrimes.size() == 0){
Log.i("", "Empty list");
}
for (Prime p : demoListOfPrimes) {
Log.i("Prime: ", p.getPrimeNo() + " ; " + p.getDateTime());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
dataSource = null;
PrimeDataSource.decrementNumberOfInstances();
}
LogCat output:
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/PrimeDataSource﹕ Insertion success
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/Prime:﹕ 1 ; 2015-04-07 22:45:18
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/Prime:﹕ 3 ; 2015-04-07 22:45:18
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/Prime:﹕ 5 ; 2015-04-07 22:45:18
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/Prime:﹕ 7 ; 2015-04-07 22:45:18
04-07 22:45:18.590 1005-1005/si.kseneman.mobile I/Prime:﹕ 11 ; 2015-04-07 22:45:18
P.S. you may need to uninstall your app or drop your table for this to work
Here is a zip of a simple project that displays the numbers in a listview activity: LINK
来源:https://stackoverflow.com/questions/29498565/datetime-does-not-show-up