I have a Tabs Activity that contains Fragments for tabs. One of the tabs displays a list of data from a SQLite databse table (or a different Fragment if the table is empty). When initially creating the tabs, the Tabs Activity checks to see if the table contains any data. If it does, then it initialises the list Fragment but doesn't set up the CursorAdapter. The CursorAdapter is initialised by an AsyncTask which syncs the SQLite database with a central database, and then creates the Cursors and CursorAdapters. The list Fragment displays a ProgressDialog while waiting for the AsyncTask to create the CursorAdapter. When the CurserAdapter is initialised, the ProgressDialog is dismissed but the ListView remains on its 'empty list' view, despite calls to notifyDataSetChanged(). If I switch tabs and come back, the ListView displays the data correctly. How can I make the ListView update once the CursorAdapter has been initialised?
Relevant bits of code:
Tabs:
private static ImageCursorAdapter friendCursorAdapter = null;
public static ImageCursorAdapter getFriendsListAdapter() {
return friendCursorAdapter;
}
public static void setFriendsListAdapter(ImageCursorAdapter adapter) {
friendCursorAdapter = adapter;
friendCursorAdapter.notifyDataSetChanged();
}
SyncTask:
protected Void doInBackground(Void... params) {
sql = "SELECT COUNT(*) FROM " + WhereWolfOpenHelper.FRIEND_TABLE_NAME;
statement = db.compileStatement(sql);
count = statement.simpleQueryForLong();
if(count>0) {
friendCursor = db.query(WhereWolfOpenHelper.FRIEND_TABLE_NAME, null, null, null, null, null, WhereWolfOpenHelper.FRIEND_FIRST_NAME_COLUMN+", "+WhereWolfOpenHelper.FRIEND_LAST_NAME_COLUMN);
}
statement.close();
}
#Override
protected void onPostExecute(Void param) {
if(friendCursor!=null) {
ImageCursorAdapter adapter = new ImageCursorAdapter(WhereWolfActivity.this, friendCursor, 0, ImageCursorAdapter.FRIENDS);
Tabs.setFriendsListAdapter(adapter);
}
}
FriendsList:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(Tabs.getFriendsListAdapter());
}
#Override
public void onStart() {
super.onStart();
if(Tabs.getFriendsListAdapter()==null) {
final ProgressDialog dialog = ProgressDialog.show(getActivity(), getString(R.string.loadingtitle), getString(R.string.loading), true, false);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(Tabs.getFriendsListAdapter()==null) {}
dialog.dismiss();
Tabs.getFriendsListAdapter().notifyDataSetChanged();
}
});
thread.start();
}
}
You are creating a new adapter, but I don't see any code to set the adapter to the list. Since the list knows nothing about your brand new adapter, calling notifyDataSetChanged() doesn't help. BTW, if you are already using fragments, you might consider using a Loader.
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 checked questions asked in StackOverflow but couldnt make to solve my issue.
I am trying to get some data in ListView in asynchronous way (to not overload UI Thread) and while i get the data i want to show a ProgressDialog. I am getting the data using ContentProvider and it loads the data successfully in ListView, however the progressDialog is not spinning, but after the list is shown with data it starts spinning and never stops (Keeps spinning).
How can i make ProgressDialog spin and dismiss() after the ListView is Visible.
Here is the activity which implements LoaderCallbacks:
public class AttractionsActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
DatabaseHelper dbHelper;
private SimpleCursorAdapter cursorAdapter;
private ListView attractionsListView;
private ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_listview);
attractionsListView = (ListView)findViewById(R.id.lvCustom);
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(false);
progressDialog.setIndeterminate(true);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dbHelper = new DatabaseHelper(this);
dbHelper.open();
populateAttractionsListView();
dbHelper.close();
}
private void populateAttractionsListView() {
//final ImageView image = (ImageView) findViewById(R.id.ivAttImage);
//Initialize Loader
getLoaderManager().initLoader(0, null, this);
String[] from = {PoisContract.COLUMN_POI_MAIN_PIC, PoisContract.COLUMN_POI_NAME};
int[] to = {R.id.ivAttImage, R.id.tvAttName};
//Initialize CursorAdapter
cursorAdapter = new SimpleCursorAdapter(this, R.layout.attractions_listview_row, null, from, to, 0);
ViewBinder viewBinder = new ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
boolean binded = false;
if(view != null){
String imgName = cursor.getString(columnIndex);
int imgResId = getResources().getIdentifier(imgName, "drawable", getPackageName());
((ImageView) view).setImageResource(imgResId);
binded = true;
}
return binded;
}
};
//Set Adapter but there is no cursor right now
attractionsListView.setAdapter(cursorAdapter);
//Set Binder to the adapter
cursorAdapter.setViewBinder(viewBinder);
//Hide ListView as there is no data returned
attractionsListView.setVisibility(View.GONE);
}
private Cursor getAllAttractions(){
ContentResolver resolver = getContentResolver();
return resolver.query(PoisContract.CONTENT_URI, PoisContract.PROJECTION, null, null, null);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
progressDialog.show(this, "Please wait...", "Loading List...");
CursorLoader cursorLoader = new CursorLoader(AttractionsActivity.this, PoisContract.CONTENT_URI, PoisContract.PROJECTION, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
cursorAdapter.swapCursor(cursor);
progressDialog.dismiss();
attractionsListView.setVisibility(View.VISIBLE);
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
//Data is not available anymore, delete reference
cursorAdapter.swapCursor(null);
}
however the progressDialog is not spinning, but after the list is
shown with data it starts spinning
This is happening because, despite (correctly)using a Loader to load the data, you also manually query the provider on the main UI thread through the call to the getAllAttractions() method(in the onCreate() method) which will block the UI thread. In fact I don't know why you did that as the returned Cursor is not used at all. Remove the:
Cursor cursor = getAllAttractions();
line from your activity's onCreate() method to solve this issue.
...and never stops (Keeps spinning).
This is happening because you call dismiss on the wrong ProgressDialog reference so you don't cancel the currently showing ProgressDialog. In the onCreate() method of the Activity you create a ProgressDialog reference, however in the onCreateLoader() callback you use the show() method which creates a new instance of a ProgressDialog(the method is static) so when you try to dismiss it in onLoadFinished() you dismiss the previously created ProgressDialog(which isn't even showing). To solve it use:
progressDialog = ProgressDialog.show(this, "Please wait...", "Loading List...");
in the onCreateLoader() callback.
As a side note you should always call a static method of a class by using the class name and not some object reference of that class, this will make thing more clear for you and also anyone who would later read your code.
Ok #Luksprog thank you very much for helping. I found the issue. The problem was in the ContentProvier AUTHORITY declaration. I misspelled the AUTHORITY String in one place. It took a day to figure out that stupid mistake.
I really appreciate your help
I am getting from time to time testing my app error:
03-04 20:57:01.929: E/TestApp(13673): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
From questions like this: Whats this exception?, and my own experience (I got this same error from time to time as in mentioned question) I would like to ask you guys what I can do to get rid of it?
As far as I know, I can do some stuff on AsyncTask connected to View, so I don't know why I am getting this info.
This is my code:
private MyDBAdapter mySQLiteAdapter;
private ListView wordList;
private AsyncDBDownload asycn;
private ProgressDialog dbUpdate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.smart_guide_ocr);
asycn = new AsyncDBDownload();
wordList = (ListView) findViewById(R.id.wordsList);
//...
}
#Override
protected void onResume() {
super.onResume();
asycn.execute(null);
}
private class AsyncDBDownload extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
try {
refreshList();//upload of contetn and set of adapter
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
return null;
}
#Override
protected void onPostExecute(String result) {
dbUpdate.dismiss();
}
#Override
protected void onPreExecute() {
dbUpdate = ProgressDialog.show(TestAppActivity.this, "Wait",
"DB download");
}
}
private void refreshList() {
mySQLiteAdapter = new MyDBAdapter(TestAppActivity.this);
mySQLiteAdapter.open();
String[] columns = { MyDBAdapter.KEY_TRANSLATED, MyDBAdapter.KEY_WORD, MyDBAdapter.KEY_LANG,
MyDBAdapter.KEY_ID };
Cursor contentRead = mySQLiteAdapter.getAllEntries(false, columns,
null, null, null, null, MyDBAdapter.KEY_ID, null);
startManagingCursor(contentRead);
Log.d(TAG, Integer.toString(contentRead.getCount()));
RowItem adapterCursor = new RowItem(this, R.layout.save_word_row,
contentRead, columns, new int[] { R.id.translatedWord, R.id.orgWord, R.id.langInfo }, 0);
wordList.setAdapter(adapterCursor);
mySQLiteAdapter.close();
}
You must not call wordList.setAdapter(adapterCursor); from within refresList method. That's a way of "changing a view from a non-UI thread".
So, instead, save the adapterCursor instance and use it from within the onPostExecute method.
You can not manipulate your Views within a background task. Do all the loading you need in your AsyncTask, pass it back into the activity in onPostExecute and set your adapter then. Doing any form of UI manipulation in a background task or service will throw this error.
Hi I'm having problem refreshing my listview after Async operation.
I have a simplecursoradapter, and custon listview and a button. Initially when application starts, it sets the listview from the data read from database. Then when user clicks a button, it starts a async code to download some data which gets inserted into a database. When async task start, I'm displaying a progressdialog, which I dismiss in postexecute(). Data is getting downloaded fine, but now how do I requery the cursor and update listview on the main thread after background job is done?
A Method "refreshRemoteData" gets called via a menu button.
This is how my AsyncTask looks like.
public class MyActivity extends ListActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onStart() {
myDBAdapter = new DBAdapter(this);
myDBAdapter.open();
populateMyList();
}
private void populateMyList() {
myCursor = myDBAdapter.fetchAllItems();
startManagingCursor(myCursor);
getListView().setAdapter(myDBAdapter);
}
private void refreshRemoteData() {
mPleaseWaitDialog = ProgressDialog.show(ExpirationDateTrackingActivity.this,
"Data", "Downloading data", true, true);
download_task = new InfoDownloaderTask();
download_task.execute();
}
private class InfoDownloaderTask extends AsyncTask<Object, String, Boolean> {
private static final String DEBUG_TAG = "InfoDownloaderTask";
protected DBAdapter mylocalDBAdapter=null;
#Override
protected void onPreExecute() {
Log.e(DEBUG_TAG, "onPreExecute: ");
mylocalDBAdapter = new DBAdapter(this);
mylocalDBAdapter.open();
}
#Override
protected void onPostExecute(Boolean result) {
Log.i(DEBUG_TAG, "onPostExecute: " );
mPleaseWaitDialog.dismiss();
mlocalDBAdapter.close();
}
#Override
protected Boolean doInBackground(Object... arg0) {
Log.v(DEBUG_TAG, "doInBackground");
///...
//Update the database
mylocalDBAdapter.insertData(....);
return true;
}
} //AsyncTask
}
I don't see my listview getting updated with new list data right after async operation is complete. But If I invoke another ativity and comeback to the listview then I see all new items (list update).
What am I missing?
You're inserting data through mylocalDBAdapter, but you aren't telling myDBAdapter about it. Try calling myDBAdapter.notifyDataSetChanged(); at the end of onPostExecute().
I have a SQLite query returning thousands of rows, which I want to visualize using a ListView.
In order to keep my UI thread responsive, I create the ListAdapter on a background thread.
However the statement that takes most time (and can cause ANR) is ListActivity.setListAdapter which I have to execute on the UI thread... Any advice?
public class CursorTestActivity extends ListActivity {
private static final String LOGTAG = "DBTEST";
private DatabaseManager mDbManager;
private Cursor mCursor;
private HandlerThread mIOWorkerThread;
private Handler mIOHandler;
private Handler mUIHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDbManager = new DatabaseManager(this);
mUIHandler = new Handler();
createIOWorkerThread();
log("creating cursor");
mCursor = mDbManager.getCursor(); // does db.query(...)
startManagingCursor(mCursor);
mIOHandler.post(new Runnable() {
#Override
public void run() {
setMyListAdapter();
}
});
log("onCreate done");
}
private void setMyListAdapter() {
log("constructing adapter");
// CustomCursorAdapter implements bindView and newView
final CustomCursorAdapter listAdapter = new CustomCursorAdapter(this,
mCursor, false);
log("finished constructing adapter");
mUIHandler.post(new Runnable() {
#Override
public void run() {
log("setting list adapter");
setListAdapter(listAdapter); // gets slower the more rows are returned
log("setting content view");
setContentView(R.layout.main);
log("done setting content view");
}
});
}
private void createIOWorkerThread() {
mIOWorkerThread = new HandlerThread("io_thread");
mIOWorkerThread.start();
Looper looper = mIOWorkerThread.getLooper();
mIOHandler = new Handler(looper);
}
private void destroyIOWorkerThread() {
if (mIOWorkerThread == null)
return;
Looper looper = mIOWorkerThread.getLooper();
if (looper != null) {
looper.quit();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mDbManager != null)
mDbManager.close();
destroyIOWorkerThread();
}
private static void log(String s) {
Log.d(LOGTAG, s);
}
}
Cursors are lazy loaded therefore at first data access the cursor is loaded - getCount is such access. The setAdapter method is invoking getCount on cursor - there is the performance issue.
You can set empty cursor in adapter and display empty list during loading cursor. Then use changeCursor method in adapter to change cursor to the new one.
You can fetch at first for example 100 rows in the first query, then load more rows in the background and changeCursor to the new one.
Or you can create own implementation of Cursor that has own implementation of getCount and fetches demanded rows on request.
consider using PagedListAdapter. it's something similar as proposed by #pawelziemba but provided by Android Framework https://developer.android.com/reference/android/arch/paging/PagedListAdapter -- if you can use Room framework you can get that for free
load data async in ViewHolder - remember to cancel work if viewHolder gets rebinded and cache loaded data if needed