I never got this working in a straightforward manner. Sorry if I'm being a little vague. I'll try to elaborate on what I'm trying to do. I am trying to build a listview that grabs its data from a webservice. Once I initialize a listview, I want to keep polling the webserver periodically and update the contents of the listview. For this I am doing something like this:
public class SampleAutoUpdateList extends Activity {
//Autoupdate handler
private Handler handler = new Handler();
private Runnable updater = new Runnable() {
public void run() {
/*
* Update the list
*/
try {
Log.i("UPDATE", "Handler called");
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
handler.postDelayed(this, Configuration.REFRESH_INTERVAL);
} catch(Exception e) {
Log.e("UPDATE ERROR", e.getMessage());
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.linearmode);
this.context = this;
searchAdapter = getFeed(URL);
LinearLayout l2 = (LinearLayout) findViewById(R.id.secondaryLayout);
ListView list = new ListView(context);
l2.addView(list);
// display UI
UpdateDisplay(list);
updater.run();
}
private SearchAdapter getFeed(String URL) {
try
{
SearchHandler handler = new SearchHandler();
URL url = new URL(URL);
String data = convertStreamToString(url.openStream());
data = data.substring(data.indexOf('['), data.length()-1);
handler.parseJSON(data);
return handler.getFeed();
}
catch (Exception ee)
{
// if we have a problem, simply return null
Log.e("getFeed", ee.getMessage());
return null;
}
}
private void UpdateDisplay(View searchView) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
searchList = (ListView) searchView;
myProgressDialog = ProgressDialog.show(this,
"Please wait...", "Loading search....", true);
new Thread() {
public void run() {
try{
Thread.sleep(2000);
} catch (Exception e) { }
runOnUiThread(new Runnable() {
#Override
public void run() {
if (searchAdapter == null)
{
Log.e("ERROR", "No Feed Available");
return;
}
searchAdapter.setContext(context);
searchList.setAdapter(searchAdapter);
searchList.setSelection(0);
}
});
// Dismiss the Dialog
myProgressDialog.dismiss();
}
}.start();
}
}
And the SearchHandler class is simple:
public class SearchHandler extends DefaultHandler {
SearchAdapter _adapter;
SearchItem _item;
public SearchHandler()
{
}
public SearchAdapter getFeed()
{
return _adapter;
}
public void parseJSON(String data) {
// TODO Auto-generated method stub
_adapter = new SearchAdapter();
JSONArray parseArray;
try {
parseArray = new JSONArray(data);
for (int i=0; i < parseArray.length(); i++) {
SearchItem item = new SearchItem();
JSONObject jsonUser = parseArray.getJSONObject(i);
item.set_from(jsonUser.getString ("from"));
item.set_msg(jsonUser.getString("msg"));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
No matter what I do, the handler gets called and the new items are fetched, but the list is never refreshed... Any ideas on what could be going wrong?
Well, it is a little bit difficult to follow your code, since you only have a fragment of it, and few of the really relevant bits. For example, based on your available code, your list should be forever empty, since you never associate the searchAdapter with a ListView...at least in the code you have shown.
That being said, the following lines seem particularly odd:
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
I am going to assume that getFeed() (not shown) creates a new ListAdapter of some sort. If getFeed() is creating a new ListAdapter, there is no need to call notifyDataSetChanged() on it, as its data set hasn't changed -- it's brand new. Moreover, unless you are associating this new ListAdapter to your ListView, the new ListAdapter will have no effect.
If I'm barking up the wrong tree, consider adding lines to your sample showing the implementation of getFeed() and where you are using searchAdapter.
Related
I know the following is probably not the best practice and not recommended to do.
I have an AsyncTask that sends data to server. The whole process that i need to do includes 4 web calls using this AsyncTask in quick succession.
I understand that with AsyncTask you must start and stop the ProgressDialog in OnPreExecute and OnPostExecute. I do normally do this.
The problem is that i call 4 AsyncTask in a row one after another, so i don't want 4 Progress dialogs repeating one after another.
I use AsyncTask.execute().get(), so they are called sequentially.
I call these AsyncTasks in a loop from the optionsMenu. What i am trying to do is set up a global ProgressDialog that i can start in the optionsMenu before the loop and cancel it after the loop.
The problem is that it doesn't show. I thought it may be because it needs to run on the UI thread so i placed it inside a Handler, but still no luck.
How can I show the progressdialog from the optionsMenu?
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menuclientassessment, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sendclientassessment:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
progressDialog2 = new ProgressDialog(ClientAssessmentActivity.this);
progressDialog2.setTitle("Connecting to Server");
progressDialog2.setMessage("Sending the assessment to server...");
progressDialog2.setIndeterminate(true);
try {
progressDialog2.show();
} catch(Exception e){
//ignore
}
}
});
for(int i = 0; i < arr.size(); i++) {
String [] params = new String[6];
AssessmentScore as = null;
as = arr.get(i);
params[0] = clientID;
params[1] = carerID;
params[2] = comments.getText().toString();
DateTime now = new DateTime();
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'H:mm");
String formattedNowTime = df.print(now);
params[3] = formattedNowTime;
params[4] = as.getElementID();
params[5] = as.getValue();
AsyncSendAssessment asa = null;
asa = new AsyncSendAssessment();
try {
asa.execute(params).get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//end of loop
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
try {
progressDialog2.dismiss();
} catch(Exception e) {
//ignore
}
}
});
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Have the progress dialog be a class variable, then instantiate it when you create the activity. That way you can access it anywhere in the application.
Create your dialog to be a class extending the class Dialog. For eg. - TestDialog. Then create a Util class with common functions using the dialog.
public class TestDialog extends Dialog {
}
Util:
public class TestDialogUtil {
public static TestDialog processingDialog;
public static void createProcessingDialog();
public static void dismissProcessingDialog();
}
Then in any of your Activities call TestDialogUtil.createProcessingDialog or TestDialogUtil.dismissProcessingDialog. You won't get extra dialogs getting created. Create a new Dialog only when processingDialog is not null.
I want to update my listview during every new item add to my arraylist during progress dialog showing this is my code
public class Load extends AsyncTask<Void, Void, Void> {
ProgressDialog progress;
#Override
protected void onPreExecute() {
progress = new ProgressDialog(SearchList.this);
progress.setMessage("loading....");
progress.show();
}
#Override
protected Void doInBackground(Void... params) {
// do tracks loading process here, don't update UI directly here
// because there is different mechanism for it
//FlowableBookViewer.webview.loadUrl("javascript:(function(){var txt = window.getSelection();window.name= txt;window.cpjs.sendToAndroid(window.name);})()");
for (int i = 0; i < Globals.currenHtmlList.size(); i++) {
try {
String pageText = FunctionsClass
.readTextFromHtml(Globals.currenHtmlList.get(i));
if (pageText.toLowerCase().contains(
Globals.SelectedText.toLowerCase())) {
String pagename = new File(
Globals.currenHtmlList.get(i)).getName();
SearchItem sitem = new SearchItem();
sitem.setTargetList(Globals.currenHtmlList.get(i));
sitem.setPageNumber(i);
if (pagename.endsWith("html")) {
pagename = pagename.substring(0,
pagename.length() - 5);
} else if (pagename.endsWith("htm")) {
pagename = pagename.substring(0,
pagename.length() - 4);
} else if (pagename.endsWith("xhtml")) {
pagename = pagename.substring(0,
pagename.length() - 6);
}
sitem.setTitleList("Page " + pagename);
founded.add(sitem);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// write display tracks logic here
progress.dismiss(); // dismiss dialog
m_adapter = new OrderAdapter(SearchList.this, R.layout.itemview,
founded);
lv.setAdapter(m_adapter);
lv.setTextFilterEnabled(true);
}
}
in this code the listview items appears after the complete of the for loop which add all the items of sitem object to founded list i want to update listview at every item added to founded .
many thanks
1) Create the adapter before you call the AsyncTask
Move this to your activity/fragment onCreate method:
m_adapter = new OrderAdapter(SearchList.this, R.layout.itemview,
founded);
lv.setAdapter(m_adapter);
lv.setTextFilterEnabled(true);
2) After you add an item in your ArrayList call the onProgressUpdate from the AsyncTask
In your doInBackground method add:
founded.add(sitem);
publishProgress();
3) In the onProgressUpdate method call the notifyDatasetChanged method from your adapter
#Override
protected void onProgressUpdate(Void... v) {
super.onProgressUpdate(v);
m_adapter.notifyDataSetChanged();
}
You can read more about notifyDataSetChanged here.
I have to refresh my listview every 3 seconds (I'm in ListFragment) so I start a new Thread wich start runOnUiThread to edit UI.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
new Thread(new Runnable() {
public void run() {
do{
ObjectMapper mapper = new ObjectMapper();
VideoData element = null;
try {
element = mapper.readValue(new URL("http://192.168.4.111:3232/videodata.json").openStream(), VideoData.class);
} catch (Exception e) {
e.printStackTrace();
}
final VideoData newData = element;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
hd = newData.getChildren().get(0).getChildren().get(id);
vba.notifyDataSetChanged();
}
});
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {}
}while(true);
}
}).start();
setAdapter();
}
I parse data on web, and update the reference.
private void setAdapter()
{
if(id == 0)
hd = hd.getChildren().get(0);
vba = new ValueBaseAdapter(getActivity(), 0, hd.getChildren());
setListAdapter(vba);
}
I saw that when notifyDataSetChanged is called, getView is called (and it's ok) but I have old reference, the one of when I called setAdapter the first time.
Also If I set null hd in run(), doesn't change anything, listview doesn't change.
Where is the error? Thanks.
It seems that notifyDataSetChanged() updates adapter's data only if you work with List
Please help with this error .... In the following code the get info function works correctly but it gives an error saying the thread caught an exception at exiting.... I am trying to use a tab host and the first tab page is the following... In this i show a progress dialog until i get my data and then show it in a list view
public class History extends Activity implements OnItemClickListener
{
/** Called when the activity is first created. */
ListView list;
//LIST OF ARRAY STRINGS WHICH WILL SERVE AS LIST ITEMS
ArrayList<String> listItems;
//DEFINING STRING ADAPTER WHICH WILL HANDLE DATA OF LISTVIEW
ArrayAdapter<String> adapter;
private String resDriver,resPassenger,ID;
private ProgressDialog dialog;
ArrayList<HashMap<String, Object>> listInfo = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> item;
JSONObject jDriver;
//JSONObject jPassenger;
// Make strings for logging
private final String TAG = this.getClass().getSimpleName();
private final String RESTORE = ", can restore state";
private final String state = "Home Screen taking care of all the tabs";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent loginIntent = getIntent();
ID = loginIntent.getStringExtra("ID");
listItems = new ArrayList<String>();
Log.i(TAG, "Started view active rides");
setContentView(R.layout.searchresults);
list = (ListView)findViewById(R.id.ListView01);
list.setOnItemClickListener(this);
adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,listItems);
list.setAdapter(adapter);
getInfo();
}
The function getInfo is used to start a thread which shows a dialog box and starts a http request to get some data ...
public void getInfo(){
GetInfoThread checkUpdate = new GetInfoThread();
checkUpdate.start();
dialog = ProgressDialog.show(History.this, "Retrieving Info","Please Wait ...", true);
}
private class GetInfoThread extends Thread
{
public void run() {
jDriver = new JSONObject();
try {
jDriver.put("ID", ID);
jDriver.put("task", "GET DATES");
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
listItems = new ArrayList<String>();
Log.i(TAG,"Sending data for the driver rides");
resDriver = HTTPPoster.sendJson(jDriver,"http://dsadsada"); // Any Server URL
JSONObject driver;
try {
driver = new JSONObject(resDriver);
Log.i(TAG,"Recieved Driver details");
listItems.add(array[0]);
handler.sendEmptyMessage(0);
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
listItems.add("No driver rides created");
handler.sendEmptyMessage(0);
}
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
dialog.dismiss();
Log.i(TAG,"hello 123");
adapter.notifyDataSetChanged();
}
};
}
I am not sure exactly what is causing your error but I suspect it has to do with UI changes not running on the actual UI thread. In Android there is a class called AsyncTask that will do the threading for you and handle the passing of data between the background thread an the UI thread. I would suggest rewriting your code to utilize the AsyncTask class.
So my code seems to run just fine until it hits this line
adapter.notifyDataSetChanged();
The error that pops up in the logcat is CalledFromWrongThreadException. The debug also shows the handler being run in the Background thread. How do I get the handler to bind to the main thread, and not the background one? I thought I just had to create the handler in the main thread, but I guess I am wrong, quite possible I am new to andriod. How do I fix this?
//Imports are included
public class DirectoryActivity extends ListActivity {
private ProgressDialog ProgressDialog = null;
private ArrayList<DirectoryListing> listing = null;
private DirectoryAdapter adapter;
private Runnable viewOrders;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.directory);
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (listing != null && listing.size() > 0) {
adapter.notifyDataSetChanged();
for (int i = 0; i < listing.size(); i++)
adapter.add(listing.get(i));
Log.e("log_tag", "\nStill running\n");
}
ProgressDialog.dismiss();
adapter.notifyDataSetChanged();
}
};
listing = new ArrayList<DirectoryListing>();
adapter = new DirectoryAdapter(this, R.layout.rows, listing);
setListAdapter(adapter);
ProgressDialog = ProgressDialog.show(DirectoryActivity.this, "Please wait...", "Retrieving data ...", true);
viewOrders = new Runnable() {
#Override
public void run() {
listing = PreparePage.getArrayList();
handler.handleMessage(null);
}
};
Thread thread = new Thread(null, viewOrders, "Background");
thread.start();
}
private static class PreparePage {
protected static ArrayList<DirectoryListing> getArrayList() {
ArrayList<DirectoryListing> listings = new ArrayList<DirectoryListing>();
JSONObject information = GetPageData.getJSONFromURL(url);
Iterator key = information.keys();
while (key.hasNext()) {
String id = (String) key.next();
JSONObject info = null;
try {
info = information.getJSONObject(id);
} catch (JSONException e) {
e.printStackTrace();
}
String name = "", title = "", photo = "";
try {
name = info.get("firstName") + " " + info.get("lastName");
title = info.getJSONObject("position").getString("name");
photo = info.optString("photoPath", "none");
} catch (JSONException e) {
e.printStackTrace();
}
listings.add(new DirectoryListing(name, title, photo));
}
return listings;
}
}
}
Try calling handler.sendEmptyMessage(0); instead of handler.handleMessage(null);
I don't know why this would cause the errors you are seeing, but this is how I have it set up when I use handler and thread instead of AsyncTask. And I have have never seen that error doing it this way.
#Nguyen is right though AsyncTask is the preferred way to handle these types of things now. And it actually makes it much easier to do.
AsyncTask docs
AsyncTask Example
In my experience, you should create your own class that extends AsyncTask class to do something at background. This is a simpler and more effectively than using thread + handler.