I have a question.
I am programming a application to add records to a Content Provider.
The content provider is working perfectly.
My problem is when I tried to insert some data into the listview. It inserts, but I'm not able to see the data(Except that I restart the app).
I was calling notifydatasetchanged from the adapter but it didn't work.
public class MainActivity extends Activity implements
LoaderManager.LoaderCallbacks<Cursor> {
ListView listContacts;
ListView listView;
Button addContactButton;
MyAdapter adapter;
ContentValues values;
ContentResolver contentResolver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentResolver = getContentResolver();
listView = (ListView) findViewById(R.id.listView1);
values = new ContentValues();
getLoaderManager().initLoader(0, null, this);
addContactButton = (Button) findViewById(R.id.button1);
addContactButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialogForm = new Dialog(v.getContext());
dialogForm.setTitle("Add a new Address");
dialogForm.setContentView(R.layout.dialog);
dialogForm.show();
Button saveButton = (Button) dialogForm
.findViewById(R.id.saveButton);
final EditText addressEditText = (EditText) dialogForm
.findViewById(R.id.addressEditText);
saveButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
try {
values.put(Contract.DATA, addressEditText.getText()
.toString());
contentResolver.insert(
Contract.CONTENT_URI, values);
values.clear();
dialogForm.dismiss();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
((MyAdapter)listView.getAdapter()).notifyDataSetChanged() ;
}
});
}
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(this, Contract.CONTENT_URI, null, null, null,
null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter = new MyAdapter(this, R.layout.list_item, cursor, 0);
listView.setAdapter(adapter);
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
Can someone give me a clue about this problem?
Also, I have another question. Is efficient the implementation of onLoadFinished method?
Greetings
You need to call restartLoader to refresh the data inside the listview.
http://developer.android.com/reference/android/app/LoaderManager.html
when you make changes to ContentProvider data, you must call getLoaderManager().restartLoader().It will discard any old data and onCreateLoader will be called again which will in turn fetch updated records and ListView will be updated.
Below is nice tutorial on Loaders.
http://www.grokkingandroid.com/using-loaders-in-android/
Related
I've found this code from here
github
I've already created a Class with a method that would take the returned value from the list and retrieve appropriate value from database based on the value.
All i need right now is an onClick handler for this code that would return me a value of the clicked item on the list.
public class MainActivity extends ListActivity {
private Cursor employees;
private MyDatabase db;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = new MyDatabase(this);
employees = db.getDisease(); // you would not typically call this on the main thread
ListAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
employees,
new String[] {"FirstName"},
new int[] {android.R.id.text1});
getListView().setAdapter(adapter);
}
#Override
protected void onDestroy() {
super.onDestroy();
employees.close();
db.close();
}
}
I have written a program to update my listview using SimpleCursorAdapter.
I have an EditText to enter mobile number and when I click on add , the mobile number is getting stored inside my sqlite database and at the same time displaying it inside the listview. But the problem is the listview is not getting refreshed , I have tried using notifyDataSetChanged() but not working.
Home_Page.java : This is the activity where I am updating my listview
try{
dbListHelper = new DriverSqliteHelper(getBaseContext());
dbListHelper.open(getBaseContext());
}catch (Exception e){
e.printStackTrace();
}
columns = new String[]{DriverSqliteHelper.DbListHelper.DRIVER_USER_ID};
to = new int[]{R.id.DriverId};
getLoaderManager().initLoader(0, null, this);
columns = new String[]{DriverSqliteHelper.DbListHelper.DRIVER_USER_ID};
to = new int[]{R.id.DriverId};
driverStatusAdapter = new DriverStatusAdapter(getBaseContext(),
R.layout.view_userid_item,null,columns,to,0);
listDriverId.setAdapter(driverStatusAdapter);
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CustomCursorLoader(getBaseContext());
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(driverStatusAdapter!=null && cursor!=null) {
driverStatusAdapter.swapCursor(cursor);
driverStatusAdapter.notifyDataSetChanged();
}else {
Log.v("Adapter null","OnLoadFinished: mAdapter is null");
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
if(driverStatusAdapter!=null) {
driverStatusAdapter.swapCursor(null);
} else {
Log.v("Adapter null", "OnLoadReset: mAdapter is null");
}
}
#Override
protected void onResume() {
super.onResume();
getLoaderManager().restartLoader(0x01, null, this);
}
I have used CustomCursorLoader class for querying the results.
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;
}
}
When I restart my app the listview is updated but not automatically when I click on add button from the same activity.
I have been stuck on this for a long time and I don't know what to do now to refresh the listview whenever I click on add.
Any help is appreciated.
I have a ListActivity that uses a CursorAdapter, and I can't get it to refresh. Here is some of the code I'm using:
public class ContactDetailsActivity extends ListActivity
{
private ContactDetailsBroadcastReceiver mLvBroadcastReceiver;
private Contact contact;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
contact = (Contact)getIntent().getSerializableExtra(AwesomeApp.CONTACT_OBJECT);
if(contact == null) {
DataUtils.log("Starting ContactDetailsActivity with NULL contact.");
Toast.makeText(this, getString(R.string.no_contact_selected), Toast.LENGTH_SHORT).show();
finish();
return;
}
DataUtils.log("Starting ContactDetailsActivity with contact:" + contact.toString());
setTitle(contact.getName());
Cursor cursor = getContactDetailsCursor();
startManagingCursor(cursor);
setListAdapter(new ContactDetailsCursorAdapter(this, cursor));
ListView lv = getListView();
lv.setBackgroundResource(R.color.white);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
Toast.makeText(ContactDetailsActivity.this, "touched", Toast.LENGTH_SHORT).show();
}
});
//Create broadcast receiver, to detect when a file upload has finished so we can update the pic status.
mLvBroadcastReceiver = new ContactDetailsBroadcastReceiver();
}
private class ContactDetailsBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
DataUtils.log("In BroadcastReceiver - ContactDetails");
refreshListView();
}
}
My adapter basically just extends CursorAdapter in which I override newView and bindView:
public class ContactDetailsCursorAdapter extends CursorAdapter
I have a method inside ContactDetailsActivity called refreshListView() which I call in onResume(), and in the onReceive() method of my BroadcastReceiver
private void refreshListView()
{
DataUtils.log("In ContactDetailsActivity refreshListView");
((ContactDetailsCursorAdapter)getListAdapter()).notifyDataSetChanged();
this.getListView().invalidate();
this.getListView().invalidateViews();
}
This is my getContactDetailsCursor() method:
private Cursor getContactDetailsCursor()
{
AwesomeDbAdapter dbAdapter = AwesomeApp.getDbAdapter(this);
Cursor c = dbAdapter.getRecentConversationsFromContact(contact.getId());
DataUtils.log("New ContactDetails cursor has " + c.getCount() + " elements");
return c;
}
As you can see, I've tried notifyDataSetChanged(), invalidate(), invalidateViews() and nothing seems to refresh the listview.
I'm totally out of ideas on how to get this to refresh. I'm seeing the logs and the method is being called both from onResume() and from the BroadcastReceiver's onReceive() methods.
Any pointers are appreciated!
This is what finally worked for me. As mentioned by #Kirill Shalnov, startManagingCursor() is deprecated however it's what I need to use. To be honest it's all a bit of guesswork, but alas does what I need.
#SuppressWarnings("deprecation")
private void refreshListView()
{
DataUtils.log("In ContactDetailsActivity refreshListView");
ContactDetailsCursorAdapter adapter = (ContactDetailsCursorAdapter)getListAdapter();
Cursor oldCursor = null;
if(adapter != null) {
oldCursor = adapter.getCursor();
}
Cursor newCursor = getContactDetailsCursor();
if(oldCursor != null) {
stopManagingCursor(oldCursor);
oldCursor.close();
}
startManagingCursor(newCursor);
adapter = null;
setListAdapter(new ContactDetailsCursorAdapter(this, newCursor));
ListView listView = getListView();
listView.setBackgroundResource(R.color.white);
listView.invalidate();
listView.invalidateViews();
}
it seems you forgot to register the receiver:
Register the receiver without registering it won't fire, register that receiver like below.
Example:
In OnCreate:
ContactDetailsBroadcastReceiver receiver=new ContactDetailsBroadcastReceiver();
IntentFilter filter=new IntentFilter("android.net.wifi.STATE_CHANGE")
registerReceiver(receiver, filter);
In OnDestroy:
unregisterReceiver(receiver);
startManagingCursor(cursor);
It is deprecated way to work with cursor, you should to use Loaders, which can reload data automatically
This is my fragment which have a listview. here data is read and entered in the listview.
I put a button here which deleted entire data of that particular table from the database. Now when I press that button, I get no response from the listview (i.e. the listview itmes are still there).
but when the fragment is restarted (either orientation changed or restarting the entire app), the data is deleted.
public class tasksListFrag extends Fragment implements View.OnClickListener {
ListView taskslist;
private ArrayList<singleRow> todoItems = new ArrayList<>();
String taskentered, dateentered, timeentered;
taskListRecycler adapter;
Button delete;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tasks_list_frag, container, false);
Log.d("HirakDebug", "tasksListFrag View Created");
taskslist = (ListView) view.findViewById(R.id.tasks_list);
adapter = new taskListRecycler(getActivity(), todoItems);
taskslist.setAdapter(adapter);
Log.d("HirakDebug", "tasksListFrag Adapter set");
delete = (Button) view.findViewById(R.id.deleteAll);
delete.setOnClickListener(this);
return view;
}
#Override
public void onResume() {
super.onResume();
Log.d("HirakDebug", "taskListFrag resumed");
tasks_Database_Operations tasksDatabaseOperations = new tasks_Database_Operations(getActivity());
String[] columns = {tasksDatabaseOperations.ASSIS_TASK, tasksDatabaseOperations.ASSIS_DATE, tasksDatabaseOperations.ASSIS_TIME};
SQLiteDatabase sqLiteDatabase = tasksDatabaseOperations.getWritableDatabase();
Cursor cursor = sqLiteDatabase.query(tasksDatabaseOperations.ASSIS_TASK_TABLE_NAME, columns, null, null, null, null, null);
while (cursor.moveToNext()) {
Log.d("HirakDebug", "Cursor Moved To Next");
int index0 = cursor.getColumnIndex(tasks_Database_Operations.ASSIS_TASK);
int index1 = cursor.getColumnIndex(tasks_Database_Operations.ASSIS_DATE);
int index2 = cursor.getColumnIndex(tasks_Database_Operations.ASSIS_TIME);
String task = cursor.getString(index0);
Log.d("HirakDebug", "tasks_Database_Operations got Task from table");
String date = cursor.getString(index1);
Log.d("HirakDebug", "tasks_Database_Operations got Date from table");
String time = cursor.getString(index2);
Log.d("HirakDebug", "tasks_Database_Operations got Time from table");
singleRow ld = new singleRow();
ld.setTask(task);
Log.d("HirakDebug", "tasks_Database_Operations setTask called");
ld.setDate(date);
Log.d("HirakDebug", "tasks_Database_Operations setDate called");
ld.setTime(time);
Log.d("HirakDebug", "tasks_Database_Operations setTime called");
todoItems.add(ld);
Log.d("HirakDebug", "tasksListFrag Data Chenged Notified");
adapter.notifyDataSetChanged();
sqLiteDatabase.close();
Log.d("HirakDebug", "tasksListFrag Database Closed");
/* tasks_Database_Operations tasksDatabaseOperations = new tasks_Database_Operations(getActivity());
Cursor cursor = tasksDatabaseOperations.readData();
String[] from = new String[]{tasksDatabaseOperations.ASSIS_TASK,
tasksDatabaseOperations.ASSIS_DATE,
tasksDatabaseOperations.ASSIS_TIME};
int[] to = new int[]{R.id.task_added, R.id.date_added, R.id.time_added};
SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(getActivity(),R.layout.single_row,
cursor,from, to);
simpleCursorAdapter.notifyDataSetChanged();
taskslist.setAdapter(simpleCursorAdapter);*/
}
}
#Override
public void onPause() {
super.onPause();
Log.d("LifeCycle", "tLF Pause");
}
#Override
public void onStop() {
super.onStop();
Log.d("LifeCycle", "tLF Stop");
}
#Override
public void onDestroyView() {
super.onDestroyView();
Log.d("LifeCycle", "tLF DestroyView");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("LifeCycle", "tLF Destroy");
}
#Override
public void onDetach() {
super.onDetach();
Log.d("LifeCycle", "tLF Detach");
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d("LifeCycle", "tLF Attach");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("LifeCycle", "tLF Create");
}
#Override
public void onStart() {
super.onStart();
Log.d("LifeCycle", "tLF Start");
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("list", (Serializable) todoItems);
Log.d("LifeCycle", "tLF saveInstance State");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
//probably orientation change
todoItems = (ArrayList<singleRow>) savedInstanceState.getSerializable("list");
} else {
if (todoItems != null) {
//returning from backstack, data is fine, do nothing
}
}
}
#Override
public void onClick(View v) {
tasks_Database_Operations tasksDatabaseOperations = new tasks_Database_Operations(getActivity());
SQLiteDatabase sqLiteDatabase = tasksDatabaseOperations.getWritableDatabase();
sqLiteDatabase.delete(tasksDatabaseOperations.ASSIS_TASK_TABLE_NAME, null, null);
Log.d("HirakDebug", "All Data Deleted");
adapter.notifyDataSetChanged();
sqLiteDatabase.close();
}
}
You are not clearing todolItems. So you need to clear it after deleting your table.
Add the below line just before adapter.notifyDataSetChanged() inside your onClick() method.
todolItems.clear();
It's a bit hard to tell from your code since you have a lot of lines per method but you don't seem to be emptying the actual ArrayList of display items when the onClick happens?
To make debugging easier you should really refactor large blocks of code into separate methods with clear names.
I've got a CursorLoader in a ListFragment (that is inside a ViewPager) that queries a database. I have a content provider for that database, which I've verified works.
The issue is this: when the app runs for the very first time a separate service calls a bulk insert in a ContentProvider:
public int bulkInsert(Uri uri, ContentValues[] values) {
if(LOGV) Log.v(TAG, "insert(uri=" + uri + ", values" + values.toString() + ")");
final SQLiteDatabase db = openHelper.getWritableDatabase();
final int match = uriMatcher.match(uri);
switch(match) {
case SNAP: {
db.beginTransaction();
for(ContentValues cv : values) {
db.insertOrThrow(Tables.SNAP, null, cv);
}
db.setTransactionSuccessful();
db.endTransaction();
getContext().getApplicationContext().getContentResolver().notifyChange(uri, null);
return values.length;
}
The CursorLoader in the list fragment returns 0 rows though on the very first run (when the database gets created). If I close and restart the app then the CursorLoader works great and returns exactly what I need. I've tried to implement waiting via a handler, but it doesn't seem to help. Here is the ListFragment that utilizes the CursorLoader:
public class DataBySnapFragment extends ListFragment implements LoaderCallbacks<Cursor> {
public static final String TAG = "DataBySnapFragment";
protected Cursor cursor = null;
private DataBySnapAdapter adapter;
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "RELOADING!!!!!");
onLoadDelay();
}
};
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
adapter = new DataBySnapAdapter(getActivity(),
R.layout.list_item_databysnap,
null,
new String[]{},
new int[]{},
0);
setListAdapter(adapter);
getLoaderManager().initLoader(0, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_databysnap, null);
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("error_workaround_1",
"workaroundforerror:Issue 19917,
http://code.google.com/p/android/issues/detail?id=19917");
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader cursorLoader = new CursorLoader(getActivity(),
Snap.CONTENT_URI,
null,
null,
null,
Snap.DATA_MONTH_TO_DATE + " DESC LIMIT 6");
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d(TAG, "data rows: " + data.getCount());
if(data.getCount() <= 0) {
delayThread();
} else {
adapter.swapCursor(data);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
private void onLoadDelay() {
getLoaderManager().initLoader(0, null, this);
}
private void delayThread() {
new Thread() {
public void run() {
longTimeMethod();
handler.sendEmptyMessage(0);
}
}.start();
}
private void longTimeMethod() {
try {
Thread.sleep(12000);
} catch (InterruptedException e) {
Log.e("tag", e.getMessage());
}
}
}
Can anyone let me know why this might be happening, or at least steer me in the right direction? Thanks!
Unless you were stalling the main thread, making a new thread and telling it to sleep wouldn't really solve anything.
I can't really say what exactly might be wrong, but it sounds like you might need to refresh your data. To test you could try to make a button with an OnClickListener which refreshes the data content, and re-pull it from the database.
Whenever your bulk insert is done you should send a message back to the fragment/activity implementing LoaderManager.LoaderCallbacks interface.
When the activity/fragment receives the message you would need to call getLoaderManager().restartLoader(0, null, this) to requery the DB.
This is what the sample app does too http://developer.android.com/guide/topics/fundamentals/loaders.html
Try to modify onLoadFinished this way:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d(TAG, "data rows: " + data.getCount());
if(data.getCount() <= 0) {
delayThread();
} else {
// set the notification for the cursor
data.setNotificationUri(getActivity().getContentResolver(), Snap.CONTENT_URI);
adapter.swapCursor(data);
}
}