I have a ListView in a fragment which I can add to from a BroadcastReceiver. However, when the app is removed from the "recents" panel (swipe the thumbnail of the app away - NOT choosing Force Stop in Settings) the BroadcastReceiver still runs (as it is supposed to do do when an app is removed from recents) but I get a Force Close dialog when it tries to update the ListView.
What I have gathered about what happens when removing an app from recents is that it does not kill the app, it just stops all the activiites. This means that the BroadcastReceivers and Services keep running. This is where my problem lies - I try to update the ListView in an Activity which has been stopped.
EDIT: I think that removing from recents causes onStop() to be called.
Do I need to create a service that update the ListView and keeps the activity running? Will it make any difference?
What I am trying to do is similar to say an SMS app. In an SMS app, a Broadcast is received and the ListView with the messages is updated to show the new message.
EDIT: Added some code
This is the Fragment which contains the ListView:
public class HistoryFragment extends FragmentBase implements OnItemAddedHandler {
ListView lv;
HistoryAdapter simpleAdpt;
int mPosition;
int index;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View histView = inflater.inflate(R.layout.history_fragment, container,
false);
setHasOptionsMenu(true);
ListView lv = (ListView) histView.findViewById(R.id.h_listView);
simpleAdpt = new HistoryAdapter();
lv.setAdapter(simpleAdpt);
return histView;
}
private class HistoryAdapter extends BaseAdapter {
private List<Map<String, Object>> mPlanetsList;
public HistoryAdapter() {
mPlanetsList = DataModel.getInstance().getPlanetList();
}
#Override
public int getCount() {
return mPlanetsList.size();
}
#Override
public Object getItem(int position) {
return mPlanetsList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = LayoutInflater.from(getActivity()).inflate(
R.layout.history_item, null);
Log.i("convertView", "was null");
}
TextView tv_title = (TextView) convertView
.findViewById(R.id.hi_tv_title); // This is part of the layout of each item
HashMap<String, String> itemDataHashMap = (HashMap<String, String>) getItem(position);
tv_title.setText(itemDataHashMap.get("planet"));
return convertView;
}
}
#Override
public void onItemAdded(Object data) {
simpleAdpt.notifyDataSetChanged();
}
#Override
public void onItemRemove(int postion) {
simpleAdpt.notifyDataSetChanged();
}
}
This is the BroadcastReceiver that I am trying to use to add items to the ListView. It is fired using an AlarmManager. This means that there is time for the user to remove the app from the recents panel before the item is added to the ListView:
public class ReminderBroadcastReceiver extends BroadcastReceiver {
// This is declared in the manifest
#Override
public void onReceive(Context context, Intent intent) {
String title = "title";
DataModel.getInstance()
.addItem(title); // Add to History
}
}
In DataModel there is:
public static DataModel getInstance() {
if (null == instance) {
Log.i("getInstance", "null");
instance = new DataModel();
}
return instance;
}
private DataModel() {
initList();
}
private void initList() {
mHistoryList = History.getList();
for (int i = 0; i < mHistoryList.size(); i++) {
mPlanetsList.add(mHistoryList.get(i).createPlanet());
}
}
public void addItem(String title) {
History history = new History();
history.getDataHashMap().put("planet", title);
history.addToHistoryDB(); // This just adds to a Database
mHistoryList.add(0, history); // Help keep the orders the same
mPlanetsList.add(0, history.createPlanet());
if (null != mOnItemAddHandler) {
mOnItemAddHandler.onItemAdded(title);
}
}
If any more code is needed, please say
Related
I'm trying to implement a loader which should load some data from a restful api using retrofit. However the content isn't loaded... unless I place a call to workspaceAdapter.notifyDataSetChanged() in my onOptionsItemSelected method. I say place a call because I don't actually have to make the call. Which I find very odd.
The second odd thing about this is that if I change some data on the server and tries to update by click the refresh menu item which should call the same workspaceAdapter.notifyDataSetChanged() nothing happens.
Below is my Activity.
public class WorkspacesActivity extends Activity implements LoaderCallbacks<List<ListviewEntry>> {
private static final String TAG = WorkspacesActivity.class.getSimpleName();
private static final int LOADER_ID = 1;
private RestClient client;
private ProgressBar progressBar;
private WorkspaceAdapter workspaceAdapter;
private ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_workspaces);
workspaceAdapter = new WorkspaceAdapter(this);
listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(workspaceAdapter);
progressBar = (ProgressBar)findViewById(R.id.workspacesProgressBar);
progressBar.setVisibility(View.VISIBLE);
Bundle extras = getIntent().getExtras();
if(extras == null) {
return;
}
String accessToken = extras.getString(MainActivity.INTENT_ACCESS_TOKEN);
if(accessToken != null) {
client = new RestClient(accessToken);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
switch (item.getItemId())
{
case R.id.action_refresh:
workspaceAdapter.notifyDataSetChanged();
// getLoaderManager().getLoader(LOADER_ID).forceLoad();
return true;
case R.id.action_create_organization:
return true;
case R.id.action_settings:
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader<List<ListviewEntry>> onCreateLoader(int i, Bundle bundle) {
Log.d(TAG, "onCreateLoader");
progressBar.setVisibility(View.VISIBLE);
return new WorkspaceLoader(this, client);
}
#Override
public void onLoadFinished(Loader<List<ListviewEntry>> listLoader, List<ListviewEntry> listviewEntries) {
Log.d(TAG, "onLoadFinished");
progressBar.setVisibility(View.GONE);
workspaceAdapter.setData(listviewEntries);
}
#Override
public void onLoaderReset(Loader<List<ListviewEntry>> listLoader) {
Log.d(TAG, "onLoaderReset");
progressBar.setVisibility(View.VISIBLE);
workspaceAdapter.setData(null);
}
}
My loader.
public class WorkspaceLoader extends AsyncTaskLoader<List<ListviewEntry>> {
private static final String TAG = WorkspaceLoader.class.getSimpleName();
private RestClient client;
public WorkspaceLoader(Context context, RestClient client) {
super(context);
this.client = client;
}
#Override
public void onStartLoading() {
Log.d(TAG, "onStartLoading");
forceLoad();
super.onStartLoading();
}
/**
* Since Organization has (1:m) Workspaces. We need to flatten this structure. A
* List<ListviewEntry> is used as internal data source. So this method request all the
* oganizations associated with a user, extracts organization or workspace id, organization or
* workspace name and stores those with a type indicating weather it's one or the other. This
* list structure can then be passed on to the adapter.
*
* #return listviewEntries
*/
#Override
public List<ListviewEntry> loadInBackground() {
Log.d(TAG, "loadInBackground");
List<Organization> organizations = client.requestOrganizations();
List<ListviewEntry> listviewEntries = new ArrayList<ListviewEntry>();
// Flatten Organizations and Workspaces
for (Organization organization : organizations) {
listviewEntries.add(new ListviewEntry(organization.getOrg_id(), organization.getName(),
ListviewEntry.Type.ORGANIZATION));
for (Workspace workspace : organization.getSpaces()) {
listviewEntries.add(new ListviewEntry(workspace.getSpace_id(), workspace.getName(),
ListviewEntry.Type.WORKSPACE));
}
}
return listviewEntries;
}
#Override
public void deliverResult(List<ListviewEntry> data) {
Log.d(TAG, "deliverResult");
if(isReset())
{
if(data != null) {
releaseResources(data);
return;
}
}
super.deliverResult(data);
}
private void releaseResources(List<ListviewEntry> data) {
Log.d(TAG, "releaseResources");
// For a simple List, there is nothing to do. For something like a Cursor,
// we would close it in this method. All resources associated with the
// Loader should be released here.
}
}
My adapter.
public class WorkspaceAdapter extends BaseAdapter {
private static final String TAG = WorkspaceAdapter.class.getSimpleName();
private List<ListviewEntry> data;
private LayoutInflater layoutInflater;
public WorkspaceAdapter(Context context) {
data = new ArrayList<ListviewEntry>();
layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<ListviewEntry> data) {
this.data = data;
}
#Override
public int getCount() {
return data.size();
}
#Override
public ListviewEntry getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
switch (getItem(position).getType()) {
case WORKSPACE:
convertView = layoutInflater.inflate(R.layout.listview_workspace, null);
holder.textView = (TextView) convertView.findViewById(R.id.listViewRow);
break;
case ORGANIZATION:
convertView = layoutInflater.inflate(R.layout.listview_organization, null);
holder.textView = (TextView) convertView.findViewById(R.id.listViewHeader);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(data.get(position).getName());
return convertView;
}
public static class ViewHolder {
public TextView textView;
}
}
Anyone got a clue about where I'm going wrong?
I've updated my setData method on my adapter si calls notifyDataSetChanged. Eg.
public void setData(List<ListviewEntry> data)
{
this.data = data;
notifyDataSetChanged();
}
That seems to do the trick. But is that how it should be done?
First, I'll preface my question with the fact that I'm not using a CursorLoader.
I'm pulling in data from a SQLlite database to populate a listview in a ListFragment. The initial load works well, but once the data is manipulated (i.e. an addition is made to the list), the listview NEVER refreshes to show the new data. I am implementing the Loader callbacks like so:
public class BillListingFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Bill>> {
private billListAdapter mAdapter;
private static final int LOADER_ID = 1;
private SQLiteDatabase mDatabase;
private BillsDataSource mDataSource;
private BillsStoreDatabaseHelper mDbHelper;
/**
* The fragment argument representing the fragment type (archive or outstanding)
*/
private static final String ARG_FRAGMENT_TYPE = "fragment_type";
/**
* Returns a new instance of this fragment based on type
*/
public static BillListingFragment newInstance(String type) {
// TODO: Make the fragment type an enum
BillListingFragment fragment = new BillListingFragment();
Bundle args = new Bundle();
args.putString(ARG_FRAGMENT_TYPE, type);
fragment.setArguments(args);
return fragment;
}
public BillListingFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.bill_view_layout, container, false);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDbHelper = new BillsStoreDatabaseHelper(getActivity());
mDatabase = mDbHelper.getWritableDatabase();
mDataSource = new BillsDataSource(mDatabase);
mAdapter = new billListAdapter(getActivity(), R.layout.bill_row_layout);
setListAdapter(mAdapter);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public Loader<List<Bill>> onCreateLoader(int id, Bundle args) {
BillDataLoader loader = new BillDataLoader(getActivity(), mDataSource);
return loader;
}
#Override
public void onLoadFinished(Loader<List<Bill>> loader, List<Bill> data) {
for(Bill bill: data){
mAdapter.add(bill);
}
setListAdapter(mAdapter);
}
#Override
public void onLoaderReset(Loader<List<Bill>> loader) {
mAdapter.clear();
}
#Override
public void onDestroy() {
super.onDestroy();
mDbHelper.close();
mDatabase.close();
mDataSource = null;
mDbHelper = null;
mDatabase = null;
}
public void reload(){
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
private class billListAdapter extends ArrayAdapter<Bill> {
Context context;
public billListAdapter(Context context, int resourceID){
super(context, resourceID);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.bill_row_layout, parent, false);
}
TextView payToField = (TextView) convertView.findViewById(R.id.nameField);
TextView dueDateField = (TextView) convertView.findViewById(R.id.overdueField);
payToField.setText(getItem(position).getPayTo());
// calculate days until due
Bill bill = getItem(position);
// TODO: Add how many days until bill in overdue field + add color
JodaTimeAndroid.init(getActivity());
DateTime dueDateDt = new DateTime(bill.getDateDue());
DateTime currentDt = new DateTime();
int daysDifference = Days.daysBetween(currentDt.toLocalDate(), dueDateDt.toLocalDate()).getDays();
// depending on what that differential looks like set text / color
if (daysDifference > 1) {
dueDateField.setText(Integer.toString(daysDifference) + " Days");
} else {
if (daysDifference == 0) {
dueDateField.setText("DUE TODAY");
} else {
if (daysDifference < 0) {
}
}
}
return convertView;
}
}
}
I have debugged my code so I know that the onLoadFinished callback is being made after the data has been manipulated. I also know that adapter contains the updated data at this point. I have tried resetting the adapter via setListAdapter(mAdatper) and every notifyDataChanged-like method I can find, but to no avail. What is going on here and how can I get the listview to update?
I have 3 view which are set to display on below the other. But the problem is that this view(the middle one) isn't showing up at all on screen.
I am not exactly sure as to how to send this list to the parent view.
This is the tutorial I'm using : http://tausiq.wordpress.com/2012/12/12/android-custom-adapter-listview-with-listfragment-and-loadermanager-inside-fragmentactivity/
Here is the code :
public class MyActivity extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.my_activity_layout, container, false);
DataListFragment list = new DataListFragment();
return view;
}
public static class DataListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Model>> {
CustomListViewAdapter mAdapter;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No Data Here");
mAdapter = new CustomListViewAdapter(getActivity());
setListAdapter(mAdapter);
setListShown(false);
// Prepare the loader.
getLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<List<Model>> onCreateLoader(int arg0, Bundle arg1) {
System.out.println("DataListFragment.onCreateLoader");
return new DataListLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<List<Model>> arg0, List<Model> data) {
mAdapter.setData(data);
System.out.println("DataListFragment.onLoadFinished");
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(Loader<List<Model>> arg0) {
mAdapter.setData(null);
}
}
public static class DataListLoader extends AsyncTaskLoader<List<Model>> {
List<Model> mModels;
public DataListLoader(Context context) {
super(context);
}
#Override
public List<Model> loadInBackground() {
System.out.println("DataListLoader.loadInBackground");
List<Model> entries = new ArrayList<Model>(5);
entries.add(new Model("1", "2","3"));
entries.add(new Model("1", "2","3"));
entries.add(new Model("1", "2","3"));
return entries;
}
/**
* Called when there is new data to deliver to the client.
*/
#Override public void deliverResult(List<Model> listOfData) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (listOfData != null) {
onReleaseResources(listOfData);
}
}
List<Model> oldApps = listOfData;
mModels = listOfData;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(listOfData);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
/**
* Handles a request to start the Loader.
*/
#Override protected void onStartLoading() {
if (mModels != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mModels);
}
if (takeContentChanged() || mModels == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
#Override protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
#Override public void onCanceled(List<Model> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* Handles a request to completely reset the Loader.
*/
#Override protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (mModels != null) {
onReleaseResources(mModels);
mModels = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
protected void onReleaseResources(List<Model> apps) {}
}
}
Here is the custom adapter:
public class CustomListViewAdapter extends ArrayAdapter<Model>{
private final LayoutInflater mInflater;
public CustomListViewAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<Model> data) {
clear();
if (data != null) {
for (Model appEntry : data) {
add(appEntry);
}
}
}
/**
* Populate new items in the list.
*/
#Override public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.single_item, parent, false);
} else {
view = convertView;
}
Model item = getItem(position);
((TextView)view.findViewById(R.id.tV_1)).setText(item.getA());
((TextView)view.findViewById(R.id.tV_2)).setText(item.getB());
((TextView)view.findViewById(R.id.tV_3)).setText(item.getC());
return view;
}
}
And this is my main activity:
public class MainActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.song_info_fragment, new TopActivity());
fragmentTransaction.add(R.id.playlist_activity_fragment, new MyActivity());
fragmentTransaction.add(R.id.controls_fragment, new BottomActivity());
fragmentTransaction.commit();
}
}
When I run the code, I only see the top and bottom activities. How can I fix this?
Ok, I was finally able to get this working.
I copied the DataListFragment class into a new class file under the same name and removed the static keywork.
Further, I copied the other section called dataListLoader into another file called DataListLoader.java.
And moved the fragment.add() transaction into the mainActivity file. And that did it!
Firstly, I want to apologize for asking this question, because I know that there are a lot similar ones here, but none of the answers solve my problem.
As the title of the question suggests I need to populate ListView with new items when data in SQLiteDatabase changes.
To be more specific...
I have an activity that shows contacts in a ListView. At the bottom of the screen I have a button that adds a contact (A Dialog pops up with fields for name, phone number etc...).
When an item in the list is clicked another Dialog is opened. In that dialog there are 3 buttons for SendSMS (to the selected contact), Edit and Delete contact.
When I fill in the form for adding new contact, or click the Delete button, I want the ListView to refresh.
It doesn't happen. In order to see the updated list I need to navigate back, and start the Contacts activity again.
Here is the code:
Activity:
public class ContactsActivity extends Activity {
private MyUtilities myUtilities;
private MyDatabaseHelper mdbh;
private AdapterContactListView contactsAdapter;
private ListView contactsListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contacts);
mdbh = new MyDatabaseHelper(this);
myUtilities = new MyUtilities(this);
contactsAdapter = new AdapterContactListView(this,mdbh);
contactsListView = (ListView)findViewById(R.id.contactActivityLV);
contactsListView.setAdapter(contactsAdapter);
contactsAdapter.notifyDataSetChanged();
contactsListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
TextView phoneNumberTV = (TextView)view.findViewById(R.id.contactElementNumberTV);
String phoneNumber = phoneNumberTV.getText().toString();
Contact contact = mdbh.getContactFromPhoneNumber(phoneNumber);
Dialog d = myUtilities.createSelectedContactOptionsDialog(contact);
d.show();
contactsAdapter.updateAdapter(mdbh.getAllContacts());
}
});
public void addContact(View view) {
Dialog d = myUtilities.createAddContactDialog();
d.show();
contactsAdapter.updateAdapter(mdbh.getAllContacts());
}
}
The Adater:
private List<Contact> allContacts;
private int numberOfContacts;
private MyDatabaseHelper mdbh;
private Context context;
public AdapterContactListView(Context c, MyDatabaseHelper m) {
super();
context = c;
mdbh = m;
allContacts = mdbh.getAllContacts();
numberOfContacts = allContacts.size();
}
public void updateAdapter(List<Contact> cs) {
allContacts = cs;
numberOfContacts = allContacts.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = convertView;
if (view == null) {
LayoutInflater li = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = li.inflate(R.layout.contact_element, null);
}
String fullName = allContacts.get(position).getFirstName();
fullName = fullName.concat(" ");
fullName = fullName.concat(allContacts.get(position).getLastName());
TextView contactName = (TextView)view.findViewById(R.id.contactElementNameTV);
TextView phoneNumber = (TextView)view.findViewById(R.id.contactElementNumberTV);
contactName.setText(fullName);
phoneNumber.setText(allContacts.get(position).getPhoneNumber());
return view;
}
The job of the dialogs is to do operations with the database (update, add, delete).
I tried placing the notifyDataSetChanged() right after updateAdapter() and in the updateAdapter() method itself, but none of that works.
One thing I think I might be missing:
The definition notifyDataSetChanged() says that it notifies attached observers. I have no attached observers, but in all the answers that I have read no one mentioned anything about attaching an observer. If you think this is the problem, please tell me how to do this.
Can someone, please, shed some light on this problem.
Thanks in advance.
To refresh your listview, either you can call a method from a list called notifydatasetchanged but that will only work when youll get the contacts in the contactsalllist. So what you need to do is that you should first get the new data in your arraylist and then call notifydataset changed on your listview.
Apart from that if you are using Contacts database I would recommend you using a simplecursoradapter instead of using the baseadapter but its completely upto you. Anyhow I have added the code for that as well. Let me know if it helps you.
/**
*
* #author Syed Ahmed Hussain
*/
public class TestListActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>, MultiChoiceModeListener {
NotificationsAdapter mNotificationAdapter;
ListView mNotificationsListView;
TextView mTxtNotificationsInfo;
Button mBtnCreateNotification;
public static final String TAG = "NotificationsList";
// ---------------------------------------------------------------------------
#Override
protected void onCreate(Bundle pSavedInstanceState) {
super.onCreate(pSavedInstanceState);
setContentView(R.layout.fragment_notifications);
initializeUIElements();
// registerForContextMenu(mNotificationsListView);
getSupportLoaderManager().initLoader(0, null, this);
mNotificationsListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
mNotificationsListView.setMultiChoiceModeListener(this);
}
// ---------------------------------------------------------------------------
/**
*
*/
private void initializeUIElements() {
mTxtNotificationsInfo = (TextView) findViewById(R.id.txtNotificationInfo);
mNotificationsListView = (ListView) findViewById(R.id.list_notifications);
mBtnCreateNotification = (Button) findViewById(R.id.btnAddNotification);
}
// ---------------------------------------------------------------------------
/**
*
*/
public void onCreateNewNotificationClick(View pV) {
Log.d(TAG, "onCreateNewNotificationClick");
Intent intent = new Intent(this, AddNewNotification.class);
startActivity(intent);
// setResult(0);
// finish();
}
// ---------------------------------------------------------------------------
#Override
public Loader<Cursor> onCreateLoader(int pId, Bundle pArgs) {
return new android.support.v4.content.CursorLoader(getApplicationContext(), NotificationsContentProvider.CONTENT_URI, null, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> pLoader, Cursor pData) {
if (pData == null || pData.getCount() == 0) {
Log.d("pData", "is null");
showTextView();
return;
}
mNotificationAdapter = new NotificationsAdapter(this, R.layout.item_notification, pData, new String[] { NotificationDatabaseHelper.COL_TITLE }, new int[] {R.id.txtNotificationTitle}, 0);
mNotificationsListView.setAdapter(mNotificationAdapter);
}
#Override
public void onLoaderReset(Loader<Cursor> pLoader) {
}
// ---------------------------------------------------------------------------
/**
* Hides the list view. shows the textview
*/
private void showTextView() {
mNotificationsListView.setVisibility(View.GONE);
mTxtNotificationsInfo.setVisibility(View.VISIBLE);
}
// ---------------------------------------------------------------------
// Multi-choice list item
#Override
public boolean onCreateActionMode(ActionMode pMode, Menu pMenu) {
// Inflate the menu for the CAB
MenuInflater inflater = pMode.getMenuInflater();
inflater.inflate(R.menu.menu_list_item, pMenu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode pMode, Menu pMenu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode pMode, MenuItem pItem) {
switch (pItem.getItemId()) {
case 1:
Toast.makeText(getApplicationContext(), pItem.getTitle(), Toast.LENGTH_SHORT).show();
pMode.finish();
break;
case 2:
Toast.makeText(getApplicationContext(), pItem.getTitle(), Toast.LENGTH_SHORT).show();
pMode.finish();
break;
default:
break;
}
return true;
}
#Override
public void onDestroyActionMode(ActionMode pMode) {
}
#Override
public void onItemCheckedStateChanged(ActionMode pMode, int pPosition, long pId, boolean pChecked) {
}
// --------------------------------------------------------------------
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.actionbar_menu_items, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
}
**
Notification Adapter is a Simple Curser Adapter:
**
/**
*
* #author Syed Ahmed Hussain
*/
public class NotificationsAdapter extends SimpleCursorAdapter {
private LayoutInflater mLayoutInflater;
private Context mContext;
private int mLayout;
public NotificationsAdapter(Context pContext, int pLayout, Cursor pC, String[] pFrom, int[] pTo, int pFlags) {
super(pContext, pLayout, pC, pFrom, pTo, pFlags);
mLayout = pLayout;
mContext = pContext;
mLayoutInflater = LayoutInflater.from(mContext);
}
#Override
public View newView(Context pContext, Cursor pCursor, ViewGroup pParent) {
return mLayoutInflater.inflate(mLayout, null);
}
}
To get it updated whenever you have a change in records do notifydatasetchange or take a refresh cursor to get all the values. Recall the method which returns you the dataset/cursor.
I'm having problems using AsyncTaskLoader. This is my first attempt populating a ListView from a SQLite database using a loader.
Everything seems ok, when I rotate the screen the data is cached and no query is done again. But when I press the home button and launch my app again, the data is loaded again.
Note: Usuario means User, so I'm populating the ListView with a list of users.
public class Main extends SherlockFragmentActivity
implements LoaderManager.LoaderCallbacks<ArrayList<Usuario>> {
UsuarioAdapter adapter;
ListView listView;
Database db;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.lista);
db = new Database(this);
adapter = new UsuarioAdapter(this, new ArrayList<Usuario>());
listView.setAdapter(adapter);
getSupportLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<ArrayList<Usuario>> onCreateLoader(int id, Bundle args) {
return new UsuariosLoader(this, db);
}
#Override
public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
ArrayList<Usuario> usuarios) {
//adapter.notifyDataSetChanged();
listView.setAdapter(new UsuarioAdapter(this, usuarios));
// ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<ArrayList<Usuario>> loader) {
listView.setAdapter(null);
}
}
// THE LOADER
class UsuariosLoader extends AsyncTaskLoader<ArrayList<Usuario>> {
private ArrayList<Usuario> usuarios;
private Database db;
public UsuariosLoader(Context context, Database db) {
super(context);
this.db = db;
}
#Override
protected void onStartLoading() {
if (usuarios != null) {
deliverResult(usuarios); // Use the cache
}
forceLoad();
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
}
#Override
public ArrayList<Usuario> loadInBackground() {
db.open(); // Query the database
ArrayList<Usuario> usuarios = db.getUsuarios();
db.close();
return usuarios;
}
#Override
public void deliverResult(ArrayList<Usuario> data) {
usuarios = data; // Caching
super.deliverResult(data);
}
#Override
protected void onReset() {
super.onReset();
// Stop the loader if it is currently running
onStopLoading();
// Get rid of our cache if it exists
usuarios = null;
}
#Override
public void onCanceled(ArrayList<Usuario> data) {
// Attempt to cancel the current async load
super.onCanceled(data);
usuarios = null;
}
}
And I think this snippet is not well done. I'm creating a new Adapter instead of updating the data.
#Override
public void onLoadFinished(Loader<ArrayList<Usuario>> loader,
ArrayList<Usuario> usuarios) {
//adapter.notifyDataSetChanged();
listView.setAdapter(new UsuarioAdapter(this, usuarios));
//((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
}
Why adapter.notifyDataSetChanged() does not work?
So, basically, my app does not crash but all my data is reloaded again every time I restart the app.
Edit: This is my Adapter code:
class UsuarioAdapter extends BaseAdapter {
private ArrayList<Usuario> usuarios;
private LayoutInflater inflater;
public UsuarioAdapter(Context context, ArrayList<Usuario> usuarios) {
this.usuarios = usuarios;
this.inflater = LayoutInflater.from(context);
}
#Override
public int getCount() { return usuarios.size(); }
#Override
public Object getItem(int pos) { return usuarios.get(pos); }
#Override
public long getItemId(int pos) { return pos; }
#Override
public View getView(int pos, View convertView, ViewGroup arg) {
LinearLayout itemView;
if (convertView == null) {
itemView = (LinearLayout) inflater.inflate(R.layout.list_item, null);
} else {
itemView = (LinearLayout) convertView;
}
ImageView avatar = (ImageView) itemView.findViewById(R.id.avatar);
TextView nombre = (TextView) itemView.findViewById(R.id.nombre);
TextView edad = (TextView)itemView.findViewById(R.id.edad);
// Set the image ... TODO
nombre.setText(usuarios.get(pos).getNombre());
edad.setText(String.valueOf(usuarios.get(pos).getEdad()));
return itemView;
}
}
The call to notifyDataSetChanged() won't change the data your adapter is using. You need to update the data the adapter has, then call that method.
NotifyDataSetChanged() will only tell the adapter it needs to create it's views, but it does not change the data. You need to handle that yourself.
In your adapter add:
public void setUsuario(List<Usuario> usuarios) {
this.usuarios = usuarios;
}
Then in onLoadFinished() call the new method, then notifyDataSetChanged().
listView.getAdapter().setUsuario(usuarios);
listView.getAdapter().notifiyDataSetChanged();
I've found the solution. The onStartLoading was the guilty:
#Override
protected void onStartLoading() {
if (usuarios != null) {
deliverResult(usuarios); // Use cache
} else {
forceLoad();
}
}
In my original post forceLoad was always called. It must be in the else branch.