I've been trying for some days to load some data from a SQLite database into a ListView inside a Fragment but I'm having this issue that I can't see the solution neither found help online.
My database consists of two tables, 'user' and 'team', being the former in a one to many relation with the later.
//User Table
public static final String CREATE_TABLE_USER =
"CREATE TABLE " + USER_TABLE + "("
+ USER_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ USER_NAME + " TEXT NOT NULL,"
+ USER_SURNAME + " TEXT NOT NULL,"
+ USER_EMAIL + " TEXT NOT NULL,"
+ USER_PHONE + " NUMBER,"
+ USER_PASSWORD + " TEXT NOT NULL)";
//Team Table
public static final String CREATE_TABLE_TEAM =
"CREATE TABLE " + TeamColumns.TEAM_TABLE + "("
+ TeamColumns.TEAM_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ TeamColumns.TEAM_NAME + " TEXT NOT NULL,"
+ TeamColumns.TEAM_COUNTRY + " TEXT NOT NULL,"
+ TeamColumns.TEAM_BOSS + " TEXT NOT NULL,"
+ TeamColumns.TEAM_DRIVER1 + " TEXT NOT NULL,"
+ TeamColumns.TEAM_DRIVER2 + " TEXT NOT NULL,"
+ TeamColumns.TEAM_CAR + " TEXT NOT NULL,"
+ TeamColumns.TEAM_POINTS + " NUMBER,"
+ TeamColumns.TEAM_ICON + " TEXT,"
+ TeamColumns.TEAM_USER_ID + " INTEGER,"
+ "CONSTRAINT userID_FK FOREIGN KEY (userID) REFERENCES " + USER_TABLE + "(id))";
So with a database structured this way, I want the ListView to only show the teams associated to the one user logged in. But i can't find how to do it, I don't really select what SQL query is used when loading info, trying to alter the cursor in the overriden bindView method does nothing and the furthest i managed to get showed the wanted rows, but the app crushed the moment i scrolled down to the bottom (I presume i just created a bunch of empty list items where usually are more items). Online search has also been of no help (sorry in advance if this question is already here) so I'm at a stalemate with this part of my app.
My question is:
Is there a way to change the SQL sentence my CustomCursorAdapter uses to load my data?
This is where I load all the data into the ListItem in the CursorAdapter class:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
//Team info placeholders
final ImageView logoView = view.findViewById(R.id.listIcon);
TextView nameText = view.findViewById(R.id.listName);
TextView countryText = view.findViewById(R.id.listCountry);
TextView directorText = view.findViewById(R.id.listDirector);
//Retrieving info
String logoURI = cursor.getString(cursor.getColumnIndexOrThrow(TeamContract.TeamColumns.TEAM_ICON));
String name = cursor.getString(cursor.getColumnIndexOrThrow(TeamContract.TeamColumns.TEAM_NAME));
String country = cursor.getString(cursor.getColumnIndexOrThrow(TeamContract.TeamColumns.TEAM_COUNTRY));
String director = cursor.getString(cursor.getColumnIndexOrThrow(TeamContract.TeamColumns.TEAM_BOSS));
//Seting the image
Glide.with(context).asBitmap()
.load(Uri.parse("file:///android_asset/" + logoURI))
.error(R.drawable.ic_baseline_error_outline_24).centerCrop().into(new BitmapImageViewTarget(logoView) {
#Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(context.getResources(), resource);
drawable.setCircular(true);
logoView.setImageDrawable(drawable);
}
});
//Setting the texts
nameText.setText(name);
countryText.setText(country);
directorText.setText(director);
}
And here where I initialize the Adapter from my Fragment class:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_team, container, false);
// Referencias UI
teamList = root.findViewById(R.id.teams_list);
mteamAdapter = new TeamAdapter(getActivity(), null);
// Setup
teamList.setAdapter(mteamAdapter);
// Eventos
teamList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Cursor currentItem = (Cursor) mteamAdapter.getItem(i);
String currentTeamId = currentItem.getString(currentItem.getColumnIndexOrThrow(TeamContract.TeamColumns.TEAM_ID));
launchTeamView(currentTeamId);
}
});
mTeamDbHelper = new DatabaseHelper(getActivity());
loadLawyers();
return root;
}
Related
I am in a fragment in Android Studio java and want to save and read something there in a SqLite database. To save, I just use this code:
databaseHu = getBaseContext().openOrCreateDatabase(databaseName_Notes, MODE_PRIVATE, null);
databaseHu.execSQL("CREATE TABLE IF NOT EXISTS " + Tabel_Hu_Name + "(subject TEXT, tasks TEXT, dayofyear INT, day INT, month INT, year INT)");
databaseHu.execSQL("INSERT INTO " + Tabel_Hu_Name + " VALUES ('" + subject.getText().toString() + "','" + tasks.getText().toString() + "','" + c.get(Calendar.DAY_OF_YEAR) + "','" + c.get(Calendar.DAY_OF_MONTH) + "','" + c.get(Calendar.MONTH) + "','" + c.get(Calendar.YEAR) + "')");
databaseHu.close();
But because I'm in a fragment, it marks that:
getBaseContext()
as a mistake.
How can I solve this? Thanks in advance!
You can use getActivity() like this,
databaseHu = getActivity().getBaseContext().openOrCreateDatabase(databaseName_Notes, MODE_PRIVATE, null);
but it may invoke nullPtrException. Instead you can override this function and create like this:
Activity activity;
#override
public void onViewCreated(View view, Bundle savedInstanceState) {
if(getActivity()==null){
activity = getActivity;
}
}
Then use it like this,
databaseHu = activity.getBaseContext().openOrCreateDatabase(databaseName_Notes, MODE_PRIVATE, null);
The database is created using a SQLiteOpenHelper:
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String SQL_CREATE_INVENTORY_TABLE = "CREATE TABLE " + InventoryEntry.TABLE_NAME + "("
+ InventoryEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ InventoryEntry.COLUMN_PRODUCT_IMAGE + " BLOB, "
+ InventoryEntry.COLUMN_PRODUCT_NAME + " TEXT NOT NULL, "
+ InventoryEntry.COLUMN_PRODUCT_PRICE + " REAL NOT NULL, "
+ InventoryEntry.COLUMN_PRODUCT_QUANTITY + " INTEGER, "
+ InventoryEntry.COLUMN_PRODUCT_DESCRIPTION + " TEXT);";
sqLiteDatabase.execSQL(SQL_CREATE_INVENTORY_TABLE);
}
All the constants are defined in a contract, this is the COLUMN_PRODUCT_IMAGE one:
public static final String COLUMN_PRODUCT_IMAGE = "image";
In a CursorAdapter, I try to access the image column from the bindView method, after adding some random data to the database:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
ImageView imageView = view.findViewById(R.id.product_image);
byte [] bytes = cursor.getBlob(cursor.getColumnIndexOrThrow(InventoryEntry.COLUMN_PRODUCT_IMAGE));
Bitmap bitmap = BitmapUtils.getBitmap(bytes);
imageView.setImageBitmap(bitmap);
}
This produces an java.lang.IllegalArgumentException: column 'image' does not exist
I double checked the database using windows cmd, here's how it looks:
sqlite> select * from inventory;
_id image name price quantity description
---------- ---------- ---------- ---------- ---------- -----------
1 ff 5.0 5 fds
Column 'image' exists, so why the error?
So i am trying to insert some data in the internal sqlite database but after i run the insert no data has been added. There are, as far as i can see no errors in the logs and every debug log i put into it is shown. If i try to run the query that is returned in the log in sqlitestudio it works without a problem so i haven't got a clue as to what is going wrong.
#Override
public void onCreate(SQLiteDatabase db) {
String SQL = pictureTable();
db.execSQL(SQL);
}
private String pictureTable() {
return "CREATE TABLE geophoto_db_pictures ( picid integer,"
+ "name varying character(50),"
+ "city varying character(20) NOT NULL,"
+ "zipcode varying character(20) NOT NULL,"
+ "country varying character(20) NOT NULL,"
+ "picdate datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+ "tags varying character(200),"
+ "image varying character(200) NOT NULL,"
+ "uploaded integer NOT NULL DEFAULT 0, PRIMARY KEY (picid))";
}
#Override
public void savePicture(Picture pic) {
Log.d(LOG_TAG, "saving picture started. Data: " + pic.getName());
// clean the inputs
String name = pic.getName();
String city = pic.getCity();
if (city != null) {
city = "'" + city + "'";
}
String country = pic.getCountry();
if (country != null) {
country = "'" + country + "'";
}
String zip = pic.getZipcode();
if (zip != null) {
zip = "'" + zip + "'";
}
String tags = tagsToString(pic.getTags());
String image = pic.getImage();
// Insert Query, all possible null values on "not null" rows will be
// replaced by a default value.
String SQL = "INSERT INTO geophoto_db_pictures(name, city, zipcode, country, tags, image)"
+ "VALUES('"
+ name
+ "',"
+ "IFNULL("
+ city
+ ", 'Unknown')"
+ ","
+ "IFNULL("
+ zip
+ ", 'Unknown')"
+ ","
+ "IFNULL("
+ country + ",'Unknown')" + ",'" + tags + "','" + image + "')";
Log.d(LOG_TAG, SQL);
executeWriteQuery(SQL);
ArrayList<Picture> list = getAllPictures();
Log.d(LOG_TAG, "Size :"+list.size());
}
private Cursor executeWriteQuery(String query){
Log.d(LOG_TAG, "execute write query");
SQLiteDatabase db = getWritableDatabase();
Cursor response = db.rawQuery(query, null);
Log.d(LOG_TAG, "write query executed");
return response;
}
All tips/help greatly appreciated!
Thomas
Try to put a semicolon at the end of table creation query. In your case as show below
private String pictureTable() {
return "CREATE TABLE geophoto_db_pictures ( picid integer,"
+ "name varying character(50),"
+ "city varying character(20) NOT NULL,"
+ "zipcode varying character(20) NOT NULL,"
+ "country varying character(20) NOT NULL,"
+ "picdate datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+ "tags varying character(200),"
+ "image varying character(200) NOT NULL,"
+ "uploaded integer NOT NULL DEFAULT 0, PRIMARY KEY (picid));";
}
While providing a query through an external String, you will need to provide SQL query with an end of statement ;. Using the primitive SQLite does not require ; as it just takes arguments and create function query itself. I have experienced both cases and I ended up understanding the way I have put it here.
The problem you are facing is that you are trying to use rawQuery() to insert a record, when you should be using execSQL() instead (see this answer).
So, the correct code for executeWriteQuery would be as follows:
private void executeWrite(String command){
Log.d(LOG_TAG, "execute write");
SQLiteDatabase db = getWritableDatabase();
db.execSQL(command);
Log.d(LOG_TAG, "write executed");
}
Also, consider using insert() instead as that will allow you to get a return value to determine whether or not the data was inserted successfully.
After delete from sqlite DB have blank row, and the next data record to the next row after empty. How to fix this problem? Before i delete some thin all works correctly, but after deleting i have this problem.
private static final String DB_CREATE =
"create table " + DB_TABLE + "(" +
COLUMN_ID + " integer primary key autoincrement, " +
COLUMN_NAME + " text, " +
COLUMN_TIME + " text, " +
COLUMN_RAW_TIME + " integer " +
");";
for deleting i use:
//method which called delRec from DB class
public boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == CM_DELETE_ID) {
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item
.getMenuInfo();
db.delRec(acmi.id);
getSupportLoaderManager().getLoader(0).forceLoad();
}
else if (item.getItemId() == RESET_STOPWATCH){
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item
.getMenuInfo();
db.resetTime(Long.toString(acmi.id));
getSupportLoaderManager().getLoader(0).forceLoad();
}
return super.onContextItemSelected(item);
}
// delete
public void delRec(long id) {
//mDB.delete(DB_TABLE, COLUMN_ID + " = " + id, null);
mDB.execSQL("DELETE FROM mytab WHERE _id = " + id);
}
for adding:
public void addRec(String txt, String txt2, long time) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_TIME, txt2);
cv.put(COLUMN_NAME, txt );
cv.put(COLUMN_RAW_TIME, time );
mDB.insert(DB_TABLE, null, cv);
The autoincrement makes the generated row ids unique. The same id won't be generated again.
If you want to generate ids as MAX(id)+1 with possible id reuse, remove the autoincrement and keep the primary key. If you want to reuse any available id number in the middle, you'll have to find it first.
Other than that, reconsider your approach. Missing id numbers in the middle should not be a problem. The problem is in the code where you display your data, not in the data itself.
Here is my onCreate method:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = getIntent();
if (intent.getData() == null)
intent.setData(Formulas.CONTENT_URI);
AutoCompleteTextView searchBar = (AutoCompleteTextView)findViewById(R.id.searchBar);
Cursor c = getContentResolver().query(getIntent().getData(),new String[] { Formulas.TITLE }, null, null, null);
SimpleCursorAdapter searchAdapter =
new SimpleCursorAdapter(this, android.R.layout.simple_dropdown_item_1line, c,
new String[] { Formulas.TITLE } , new int[] { android.R.id.text1} );
searchBar.setAdapter(searchAdapter);
}
I did input some sample data in my databaseopenhelper:
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE formula ( \n" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
"title TEXT NOT NULL, \n" +
"formula TEXT NOT NULL \n" +
");");
db.execSQL("INSERT INTO formula VALUES ( \n" +
"null, \"Pythagorean theorium\", \"a^2+b^2=c^2\" \n" +
"); ");
db.execSQL("INSERT INTO formula VALUES ( \n" +
"null, \"Perimeter of a square\", \"l^2 * w^2\" \n" +
"); ");
db.execSQL("INSERT INTO formula VALUES ( \n" +
"null, \"Area of a square\", \"l^4\" \n" +
"); ");
}
The problem is where I set the adapter to the AutoCompleteTextView, I expect the values that I inserted using the SQLiteDatabase.execSQL(String) method. But nothing is there? The dropdown box doesn't even show up? I used debugging and found that the Cursor was not null and neither was the adapter or AutoCompleteTextView? What could be the problem? I already defined my own contentprovider.
The query returned nothing because the content uri was faulty and completely my fault. Here is the relavent thread.