I am making an android recipes application where i use a database.
In database there is a column named" "images", where i store the name of the file of the picture of the recipe where i store at drawable folder.
Now i want to make a List with the recipes, showing:
1) the Title of the recipe
2) a short description
and
3) an image of the recipe
To do that i use a Simplecursoradaptor.
My problem is i can not show the image.
I want to read the file name from the column "images" and then set the image at my imageview (R.id.imageview1)
Here is my code untill now:
public class RecipesMainActivity extends Activity
{
public static final String ROW_ID = "row_id"; //Intent extra key
private ListView esodaListView; // the ListActivity's ListView
private SimpleCursorAdapter esodaAdapter; // adapter for ListView
DatabaseConnector databaseConnector = new DatabaseConnector(RecipesMainActivity.this);
// called when the activity is first created
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipes_main);
esodaListView = (ListView)findViewById(R.id.esodaList);
esodaListView.setOnItemClickListener(viewEsodaListener);
databaseConnector.open();
// map each esoda to a TextView in the ListView layout
// The desired columns to be bound
String[] from = new String[] {"title","ingredients","image"}; // built an String array named "from"
//The XML defined views which the data will be bound to
int[] to = new int[] { R.id.esodaTextView, R.id.amountTextView, R.id.imageView1}; // built an int array named "to"
// EsodaMainActivity.this = The context in which the ListView is running
// R.layout.esoda_list_item = Id of the layout that is used to display each item in ListView
// null =
// from = String array containing the column names to display
// to = Int array containing the column names to display
esodaAdapter = new SimpleCursorAdapter (this, R.layout.recipe_list_item, null, from, to);
esodaListView.setAdapter(esodaAdapter); // set esodaView's adapter
} // end of onCreate method
#Override
protected void onResume()
{
super.onResume(); // call super's onResume method
// create new GetEsodaTask and execute it
// GetEsodaTask is an AsyncTask object
new GetEsodaTask().execute((Object[]) null);
} // end of onResume method
// onStop method is executed when the Activity is no longer visible to the user
#Override
protected void onStop()
{
Cursor cursor= esodaAdapter.getCursor(); // gets current cursor from esodaAdapter
if (cursor != null)
cursor.deactivate(); // deactivate cursor
esodaAdapter.changeCursor(null); // adapter now has no cursor (removes the cursor from the CursorAdapter)
super.onStop();
} // end of onStop method
// this class performs db query outside the GUI
private class GetEsodaTask extends AsyncTask<Object, Object, Cursor>
{
// we create a new DatabaseConnector obj
// EsodaMainActivity.this = Context
DatabaseConnector databaseConnector = new DatabaseConnector(RecipesMainActivity.this);
// perform the db access
#Override
protected Cursor doInBackground(Object... params)
{
databaseConnector.open();
// get a cursor containing call esoda
return databaseConnector.getAllEsoda();
// the cursor returned by getAllContacts() is passed to method onPostExecute()
} // end of doInBackground method
// here we use the cursor returned from the doInBackground() method
#Override
protected void onPostExecute(Cursor result)
{
esodaAdapter.changeCursor(result); // set the adapter's Cursor
databaseConnector.close();
} // end of onPostExecute() method
} // end of GetEsodaTask class
I searched a lot online but i couldnt find something tha could help me.
Can i set an image at an imageview with simplecursoradaptor or not?
Do i have to make a custom cursor adaptor? And if i have to make a custom one how can i do it?
You need to set the ViewBinder of the adapter to set the image of the ImageView to the value received from the DB.
esodaAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue (View view, Cursor cursor, int columnIndex){
if (view.getId() == R.id.imageView1) {
ImageView IV=(ImageView) view;
int resID = getApplicationContext().getResources().getIdentifier(cursor.getString(columnIndex), "drawable", getApplicationContext().getPackageName());
IV.setImageDrawable(getApplicationContext().getResources().getDrawable(resID));
return true;
}
return false;
}
Related
I got this error in my code say there is an error in doInBackground method while I don't use AsyncTask in my code, instead, I am using Loader to load the data from SQLite database, How I could get an error in a method I didn't use or call in my code at any place.
the error occurs when I am trying to press the floatingactionbutton to open another activity, I have two modes for this activity depend on the user going to update or add a pet.
package com.example.android.pets;
import android.app.LoaderManager;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import com.example.android.pets.data.PetContract.PetEntry;
/**
* Displays list of pets that were entered and stored in the app.
*/
public class CatalogActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
//loader unique ID
public static final int PET_LOADER = 0;
// If non-null, this is the current filter the user has provided.
private String mCurFilter;
// This is the Adapter being used to display the list's data.
private PetCursorAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog);
// Setup FAB to open EditorActivity
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(CatalogActivity.this,EditorActivity.class));
}
});
// Find the ListView which will be populated with the pet data
ListView petListView = (ListView) findViewById(R.id.list);
// Find and set empty view on the ListView, so that it only shows when the list has 0 items.
View emptyView = findViewById(R.id.empty_view);
petListView.setEmptyView(emptyView);
mAdapter = new PetCursorAdapter(this,null);
petListView.setAdapter(mAdapter);
getLoaderManager().initLoader(PET_LOADER,null,this);
petListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent intent = new Intent(CatalogActivity.this,EditorActivity.class);
Uri contentUri = ContentUris.withAppendedId(PetEntry.CONTENT_URI,id);
intent.setData(contentUri);
startActivity(intent);
}
});
}
/**
* Helper method to insert hardcoded pet data into the database. For debugging purposes only.
*/
private void insertPet() {
// Create a ContentValues object where column names are the keys,
// and Toto's pet attributes are the values.
ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, "Toto");
values.put(PetEntry.COLUMN_PET_BREED, "Promenader");
values.put(PetEntry.COLUMN_PET_GENDER, PetEntry.GENDER_MALE);
values.put(PetEntry.COLUMN_PET_WEIGHT, 7);
// Insert a new row for Toto into the provider using the ContentResolver.
// Use the {#link PetEntry#CONTENT_URI} to indicate that we want to insert
// into the pets database table.
// Receive the new content URI that will allow us to access Toto's data in the future.
Uri newUri = getContentResolver().insert(PetEntry.CONTENT_URI, values);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_catalog.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_catalog, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Insert dummy data" menu option
case R.id.action_insert_dummy_data:
insertPet();
return true;
// Respond to a click on the "Delete all entries" menu option
case R.id.action_delete_all_entries:
// Do nothing for now
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(PetEntry.CONTENT_URI,
Uri.encode(mCurFilter));
} else {
baseUri = PetEntry.CONTENT_URI;
}
String [] projection = {PetEntry._ID,PetEntry.COLUMN_PET_NAME,PetEntry.COLUMN_PET_BREED};
return new CursorLoader(getApplication(), baseUri,
projection, null, null,
null );
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}.
Blockquote
public class EditorActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
/** Create an instance from #link{PetDbHelper}*/
PetDbHelper mDbHelper;
/** EditText field to enter the pet's name */
private EditText mNameEditText;
/** Content URI for the existing pet (null if it's a new pet) */
private Uri mCurrentPetUri;
/** EditText field to enter the pet's breed */
private EditText mBreedEditText;
/** EditText field to enter the pet's weight */
private EditText mWeightEditText;
/** EditText field to enter the pet's gender */
private Spinner mGenderSpinner;
//the unique id of the loader
private final static int EXISTING_PET_LOADER = 0;
/**
* Gender of the pet. The possible values are:
* 0 for unknown gender, 1 for male, 2 for female.
*/
private int mGender = 0;
// get the title of the activity inside a variable
private CharSequence activityTitle = getTitle();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_editor);
Intent intent = getIntent();
mCurrentPetUri = intent.getData();
if (mCurrentPetUri == null){
setTitle(R.string.editor_activity_title_new_pet);
}else {
setTitle(R.string.editor_activity_edit_pet);
}
// Find all relevant views that we will need to read user input from
mNameEditText = (EditText) findViewById(R.id.edit_pet_name);
mBreedEditText = (EditText) findViewById(R.id.edit_pet_breed);
mWeightEditText = (EditText) findViewById(R.id.edit_pet_weight);
mGenderSpinner = (Spinner) findViewById(R.id.spinner_gender);
// To access our database, we instantiate our subclass of SQLiteOpenHelper
// and pass the context, which is the current activity.
mDbHelper = new PetDbHelper(this);
getLoaderManager().initLoader(EXISTING_PET_LOADER, null, this);
setupSpinner();
}
/**
* Setup the dropdown spinner that allows the user to select the gender of the pet.
*/
private void setupSpinner() {
// Create adapter for spinner. The list options are from the String array it will use
// the spinner will use the default layout
ArrayAdapter genderSpinnerAdapter = ArrayAdapter.createFromResource(this,
R.array.array_gender_options, android.R.layout.simple_spinner_item);
// Specify dropdown layout style - simple list view with 1 item per line
genderSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
// Apply the adapter to the spinner
mGenderSpinner.setAdapter(genderSpinnerAdapter);
// Set the integer mSelected to the constant values
mGenderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String selection = (String) parent.getItemAtPosition(position);
if (!TextUtils.isEmpty(selection)) {
if (selection.equals(getString(R.string.gender_male))) {
mGender = PetEntry.GENDER_MALE; // Male
} else if (selection.equals(getString(R.string.gender_female))) {
mGender = PetEntry.GENDER_FEMALE; // Female
} else {
mGender = PetEntry.GENDER_UNKNOWN; // Unknown
}
}
}
// Because AdapterView is an abstract class, onNothingSelected must be defined
#Override
public void onNothingSelected(AdapterView<?> parent) {
mGender = 0; // Unknown
}
});
}
/**
* Get user input from editor and save new pet into database.
*/
private void savePet() {
// Read from input fields
// Use trim to eliminate leading or trailing white space
String nameString = mNameEditText.getText().toString().trim();
String breedString = mBreedEditText.getText().toString().trim();
String weightString = mWeightEditText.getText().toString().trim();
// If the weight is not provided by the user, don't try to parse the string into an
// integer value. Use 0 by default.
int weight = 0;
if (!TextUtils.isEmpty(weightString)) {
weight = Integer.parseInt(weightString);
}
if (mCurrentPetUri == null &&
TextUtils.isEmpty(nameString) && TextUtils.isEmpty(breedString) &&
TextUtils.isEmpty(weightString) && mGender == PetEntry.GENDER_UNKNOWN) {return;}
// Create a ContentValues object where column names are the keys,
// and pet attributes from the editor are the values.
ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, nameString);
values.put(PetEntry.COLUMN_PET_BREED, breedString);
values.put(PetEntry.COLUMN_PET_GENDER, mGender);
values.put(PetEntry.COLUMN_PET_WEIGHT, weight);
// Determine if this is a new or existing pet by checking if mCurrentPetUri is null or not
if (mCurrentPetUri == null) {
// This is a NEW pet, so insert a new pet into the provider,
// returning the content URI for the new pet.
Uri newUri = getContentResolver().insert(PetEntry.CONTENT_URI, values);
// Show a toast message depending on whether or not the insertion was successful.
if (newUri == null) {
// If the new content URI is null, then there was an error with insertion.
Toast.makeText(this, getString(R.string.editor_insert_pet_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the insertion was successful and we can display a toast.
Toast.makeText(this, getString(R.string.editor_insert_pet_successful),
Toast.LENGTH_SHORT).show();
}
}else {
// Otherwise this is an EXISTING pet, so update the pet with content URI: mCurrentPetUri
// and pass in the new ContentValues. Pass in null for the selection and selection args
// because mCurrentPetUri will already identify the correct row in the database that
// we want to modify.
int rowsAffected = getContentResolver().update(mCurrentPetUri, values, null, null);
// Show a toast message depending on whether or not the update was successful.
if (rowsAffected == 0) {
// If no rows were affected, then there was an error with the update.
Toast.makeText(this, getString(R.string.editor_update_pet_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the update was successful and we can display a toast.
Toast.makeText(this, getString(R.string.editor_update_pet_successful),
Toast.LENGTH_SHORT).show();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_editor.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_editor, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Save" menu option
case R.id.action_save:
//save pet for data base
savePet();
finish();
return true;
// Respond to a click on the "Delete" menu option
case R.id.action_delete:
// Do nothing for now
return true;
// Respond to a click on the "Up" arrow button in the app bar
case android.R.id.home:
// Navigate back to parent activity (CatalogActivity)
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader <Cursor> onCreateLoader(int id, Bundle args) {
// Since the editor shows all pet attributes, define a projection that contains
// all columns from the pet table
String[] projection = {
PetEntry._ID,
PetEntry.COLUMN_PET_NAME,
PetEntry.COLUMN_PET_BREED,
PetEntry.COLUMN_PET_GENDER,
PetEntry.COLUMN_PET_WEIGHT };
// This loader will execute the ContentProvider's query method on a background thread
return new CursorLoader(this, // Parent activity context
mCurrentPetUri, // Query the content URI for the current pet
projection, // Columns to include in the resulting Cursor
null, // No selection clause
null, // No selection arguments
null); // Default sort order
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor.moveToFirst()) {
// Find the columns of pet attributes that we're interested in
int nameColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_NAME);
int breedColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_BREED);
int genderColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_GENDER);
int weightColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_WEIGHT);
// Extract out the value from the Cursor for the given column index
String name = cursor.getString(nameColumnIndex);
String breed = cursor.getString(breedColumnIndex);
int gender = cursor.getInt(genderColumnIndex);
int weight = cursor.getInt(weightColumnIndex);
// Update the views on the screen with the values from the database
mNameEditText.setText(name);
mBreedEditText.setText(breed);
mWeightEditText.setText(Integer.toString(weight));
// Gender is a dropdown spinner, so map the constant value from the database
// into one of the dropdown options (0 is Unknown, 1 is Male, 2 is Female).
// Then call setSelection() so that option is displayed on screen as the current selection.
switch (gender) {
case PetEntry.GENDER_MALE:
mGenderSpinner.setSelection(1);
break;
case PetEntry.GENDER_FEMALE:
mGenderSpinner.setSelection(2);
break;
default:
mGenderSpinner.setSelection(0);
break;
}
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// If the loader is invalidated, clear out all the data from the input fields.
mNameEditText.setText("");
mBreedEditText.setText("");
mWeightEditText.setText("");
mGenderSpinner.setSelection(0); // Select "Unknown" gender
}
}.
08-04 04:28:57.457 11642-11900/com.example.android.pets E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #7
Process: com.example.android.pets, PID: 11642
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:353)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: uri
at com.android.internal.util.Preconditions.checkNotNull(Preconditions.java:128)
at android.content.ContentResolver.query(ContentResolver.java:737)
at android.content.ContentResolver.query(ContentResolver.java:704)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:64)
at android.content.CursorLoader.loadInBackground(CursorLoader.java:54)
at android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:315)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:69)
at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:64)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
You aren't using asynctask directly, but the AsyncTaskLoader class is using it. You have to learn reading the logs. There says 'Dude, you aren't sending anything in the URI, and therefore the background process is failing'.
I have written a program to add mobile no into my sqlite database on a button click which is working properly , I am also using a listview to show the data added for which I am using a CustomCursorLoader class to query my results .
The problem which I am facing is , suppose I have nothing in my database so the cursor count is 0 but when I insert a data for the first time , the cursor count should become 1 but it shows 0 , and then again when I insert another data at that moment i am getting cursor count as 1 but the data which was previously inserted is being shown in the listview
Posting my code
public class Home_Page extends Activity implements
LoaderManager.LoaderCallbacks<Cursor> {
DriverStatusAdapter driverStatusAdapter;
ListView listDriverId;
private static final int URL_LOADER = 0;
CustomCursorLoader loader = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
try{
dbListHelper = new DriverSqliteHelper(getBaseContext());
dbListHelper.open(getBaseContext());
}catch (Exception e){
e.printStackTrace();
}
String[] columns = new String[]
{DriverSqliteHelper.DbListHelper.DRIVER_USER_ID};
int[] to = new int[]{R.id.DriverId};
driverStatusAdapter = new DriverStatusAdapter(getBaseContext(),
R.layout.view_userid_item,null,columns,to,0);
listDriverId = (ListView) findViewById(R.id.driverIDList);
listDriverId.setAdapter(driverStatusAdapter);
registerForContextMenu(listDriverId);
Log.i("LoaderManager", "Started on activity start");
getLoaderManager().initLoader(0, null, Home_Page.this);
txtAdd.setOnClickListener(new View.OnClickListener() {
String userId = edtUserId.getText().toString();
if (userId.equals(""))
{
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Please
enter user id", Snackbar.LENGTH_LONG);
View sbView = snackbar.getView();
TextView textView = (TextView)
sbView.findViewById(android.support.design.R.id.
snackbar_text);
snackbar.show();
}
else{
sendUserStatus(); ///// method to send mobile no to server
//// if status received from server is ok then i am inserting
////the data into the database
Log.i("LoaderManager", "Restarted on button click");
getLoaderManager().restartLoader(0, null, Home_Page.this);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
switch (i){
case URL_LOADER:
Log.i("Case URL Loader", "Custom Cursor Loader called");
loader = new CustomCursorLoader(getBaseContext());
return loader;
default:
Log.i("Case default", "Default Case called");
return null;
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
Log.i("LoaderManager", "Finished load entry... - Cursor: " +
cursor.getCount());
this.loader = (CustomCursorLoader)loader;
driverStatusAdapter.changeCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.i("LoaderManager", "Resetting loader...");
driverStatusAdapter.changeCursor(null);
}
}
CustomCursorLoader.java
public class CustomCursorLoader extends CursorLoader{
Context context;
DriverSqliteHelper driverSqliteHelper;
Cursor cursor;
public CustomCursorLoader(Context context) {
super(context);
try {
driverSqliteHelper = new DriverSqliteHelper(context);
driverSqliteHelper.open(context);
}catch (Exception e){
e.printStackTrace();
}
}
public Cursor loadInBackground(){
cursor = driverSqliteHelper.getDriverStatus();
return cursor;
}
}
My Logcat
I/LoaderManager﹕ Started on activity start
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 2
********on my first button click ********
I/LoaderManager﹕ Restarted on button click
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 2
********* on my second button click ********
I/LoaderManager﹕ Restarted on button click
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 3
I want my cursor count to change on first button click itself , can anyone suggest me what changes do i need to make ?
Ok i have found the solution myself , i put the getLoaderManager().restartLoader(0, null, Home_Page.this); inside sendUserStatus() method where i am also inserting the data.
Now the cursor count is incrementing and the listview is also getting updated automcatically
I have a view with a button and a list view backed by a cursor adapter containing bindView() and newView() for customized views. Each row of a list contains a Text and a checkbox. The data for each view comes from the database. I'm passing my Database adapter in the cursor adapter constructor. This I use to update the database when a checkbox is check or unchecked (works well). Of course I run "re-query" on cursor and view.refreshDrawableState()). Is this a good idea? What would be a better solution?
Second problem more serious, when a Button is clicked it starts a new activity. After hitting the back button from the new activity I get back my list View. But when I try to click on the checkbox this time I get Database close exception. Why? How do I fix this error?
Following is the list view and code snippet.
Button --------> Starts a new activity
CheckBox | TextView
CheckBox | TextView
MyActivity.java
onCreate() {
...
Button add_item_btn = (Button) findViewById(R.id.add_item_btn_id);
add_item_btn.setOnclickListener(new OnClickListener() {
//Start a new activity
});
}
protected void onPause() {
adapter.close();
mCursor.close();
}
protected void onResume() {
mListView = getListView();
adapter = new DBAdapter(getApplication());
adapter.open();
mCursor = adapter.getAllItems();
mCustomAdapter = new MyCursorAdapter(MyActivity.this, mCursor, adapter);
mListView.setAdapter(mCustomAdapter);
}
MyCursorAdapter.java
public class MyCursorAdapter extends CursorAdapter {
Cursor mCursor;
DBAdapter adapter;
public MyCursorAdapter(Context context, Cursor c, DBAdapter _adapter) {
...
mCursor = c;
adapter = _adapter;
}
public void bindView(final View view, Context context, final Cursor cursor) {
final CheckBox itemStatusCB = (CheckBox)
view.findViewById(R.id.item_status_id);
idx = cursor.getColumnIndex(myItem.ITEM_STATUS);
final long itemStatus = cursor.getLong(idx);
if (itemStatus == 1) {
itemStatusCB.setChecked(true);
} else {
itemStatusCB.setChecked(false);
}
itemStatusCB.setOnClickListener(new OnClickListener() {
#Override public void onClick(View v) {
int newStatus = 0;
if (((CheckBox) v).isChecked()) {
newStatus = 1;
}
adapter.updateItemStatus(itemId, newStatus);
mCursor.requery();
view.refreshDrawableState();
});
}
}
}
I was able to solve this. The new activity which was called had a DB connection open on onStart() and DB close on onDestroy(). After returning from that activity I was getting Database Illegal state Exception error as described with stack trace. I think it was returning cached version of DB connection. Once I removed DB.close() from the guest activity, it stopped issuing database not open error. Normally you would think that every activity can open a DB connection in it's onResume() or onStart() and close it in it's onPause() or onStop() or onDestroy() and it won't affect the connection across activities. Does this Make sense?
i have a Main activity which has a list view.
the listview items are loaded with LoaderManager.
when i click an item in the listview i open another activity that shows more information
(with "startActivityForResult")
the problem is :
when i go back(using the return key on) from the activity that shows information to the main activity and then click again i get an exception - Attempt to re-open an already closed object.
but if i go back from that activity(that shows more information) with a Button that i made there(which is actually "finish()") to the main activity and then cllick again then i get no exception
anyone knows what is the problem?
Thanks !
private void display_listview()
{
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(
this,
R.layout.row_invite_layout,
null,
columns,
to,
0);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//Ensures a loader is initialized and active.
getSupportLoaderManager().initLoader(0, null, this);
//add implementation to listview item click
listView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
IInvite invite = LoadFromCursor(cursor);
cursor.close();
mUpdateFlag = true;
if(!mTrashFlag) // Trash Can is turned off
{
Intent intent = new Intent(getApplicationContext(), InviteViewActivity.class);
intent.putExtra("invite",(Invite)invite);
startActivityForResult(intent, INVITE_REQUEST_ID);
}
else // Trash Can is turned on
{
getContentResolver().delete(InviteContentProvider.CONTENT_URI, SQLdataHelper.KEY_ROWID+"="+invite.getID(), null);
ExtraDelete(invite);
getSupportLoaderManager().getLoader(0).forceLoad();
}
}
});
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
dataAdapter.swapCursor(null);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
String[] projection = {
SQLdataHelper.KEY_ROWID,
SQLdataHelper.INVITE_CLIENT_ID,
SQLdataHelper.INVITE_CREATION_DATE,
SQLdataHelper.INVITE_NAME,
SQLdataHelper.INVITE_NOTE,
SQLdataHelper.INVITE_REQUESTED_DATE,
SQLdataHelper.INVITE_TOTAL_PRICE,
SQLdataHelper.INVITE_STATUS
};
CursorLoader cursorLoader = new CursorLoader(this,
InviteContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
UPDATE: FIXED IT. i added this method to my main activity..
#Override
public void onResume()
{
super.onResume();
getSupportLoaderManager().getLoader(0).forceLoad();
}
You can do it by creating public static fields, but I wouldn't recomend it. You can store data in shared preferences and then retrieve it whenever you want.
I was browsing the net for 2 days allready and tryed alot of stuff but can't seem to figure out what is wrong with this.
I am still fairly new to the Android deevelopment so I probably missed something obvious.
I have an app witch is using a sqllite databse to store some data and for the porpose of this Proof of concept displaying that in a listview. I can add items to the list, delete them.
So far so good. The problem I have is when I instead of delete update a column in the databse called "deleted" and set it to 1 and then have the adapter to update the list. It seems not to work.
If I use the delete statement it works. It updates and everything is fine but I whant to have the deleted items in the database but not to show them (So basicly "hiding" items)
If I check the database the update itself succeded the column changes and everything so I guess it is a refresh problem because the adapter does not requery the database or something in that direction
Listview Loader:
public void fillData() {
if(lw.getAdapter() == null){
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
to, 0);
Log.v("Count",Integer.toString(cursor.getCount()));
lw.setAdapter(adapter);
}
else
adapter.notifyDataSetChanged();
}
Delete functon
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
/* Code for actual delete
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
*/
/* Code for update and hide */
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
+ info.id);
ContentValues values = new ContentValues();
values.put(TodoTable.COLUMN_DIRTY, 1);
values.put(TodoTable.COLUMN_DELETED, 1);
getContentResolver().update(uri,values,null,null);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
if I put a log to the ContentProvider's query function it actually does not fire.
Any suggestions on how to figure this out?
If I use adapter.swapCursor(cursor); it works fine just just don't know if this is the correct way of doing this.
public void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
if(lw.getAdapter() == null){
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
to, 0);
Log.v("Count",Integer.toString(cursor.getCount()));
lw.setAdapter(adapter);
}
else
{
adapter.swapCursor(cursor);
}
}
Ty for the help
Using adapter.swapCursor(cursor) is correct so you're almost there in answering your own question.
Your first piece of code doesn't work because when you call fillData() after your database update, you simply call adapter.notifyDataSetChanged() and the dataset hasn't actually changed because the cursor is the same. A cursor is a reference to rows from your database and updating the underlying database doesn't refresh the cursor. Your second piece of code does refresh the cursor and swaps the new one in to the adapter (which also triggers an update to the view it is bound to).
The more common way to code this is:
Add this interface to your activity:
public class MyActivity extends Activity implementsLoaderManager.LoaderCallbacks<Cursor>
In onCreate, set up the adapter (note that the cursor is null at this point):
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
int[] to = new int[] { R.id.label };
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0); //Note that the cursor is null
lw.setAdapter(adapter);
Initiate the loader:
getLoaderManager().initLoader(0, null, this);
This calls onCreateLoader in a background thread (so if your query is long running it won't block the UI thread). When it finishes, onLoadFinished is called on the UI thread where you can swap in the new cursor.
After you do a delete or update, restart the loader:
getLoaderManager().restartLoader(0, null, this);
This calls onLoaderReset which removes the existing cursor from the adapter and then calls onCreateLoader again to swap in a new one.
Finally add these methods:
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Loader<Cursor> loader = new CursorLoader(this, TodoContentProvider.CONTENT_URI, from, where, new String[] {"0"}, null);
return loader;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
adapter.swapCursor(cursor);
}
public void onLoaderReset(Loader<Cursor> loader)
{
adapter.swapCursor(null);
}
Here below is my working solution. Briefly, I am updating the underlying database in a service and when the service finishes its job it calls the activity with a localbroadcastmanager. I use List and BaseAdapter.
In the service, I call:
Intent intent = new Intent("notifyactivity");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
In the activity:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,new IntentFilter("notifyactivity"));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
applicationcontacts.clear(); //clear the list first
applicationcontacts.addAll(db.getAllContacts()); //reload the list
listview=(ListView) findViewById(R.id.listview1);
listview.setAdapter(listadaptor);
runOnUiThread(new Runnable() {
#Override
public void run() {
listadaptor.notifyDataSetChanged();
}
});
}
};
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
}