I'm using a ListActivity and am filling the list view with an adapter as such:
// SimpleAdapter adapter = ...
setListAdapter( adapter );
setContentView( R.layout.main );
ListView lv = getListView();
lv seems to have zero children, even though I'm populating it with children from a layout and the adapter has many items in it. I noticed that if I wait some time -- when the list view is rendered fully -- it does have children. How can I get notified of when the list view is fully ready to be used?
You can populate your list in the background using an AsyncTask, and then notify yourself once the loading is complete.
class MyTask extends AsyncTask<Context, Integer, String> {
m_loaded = false;
#Override
protected String doInBackground(Context... params) {
Log.d("doinBackground", "here");
// do your loading
return "";
}
// -- gets called just before thread begins
#Override
protected void onPreExecute() {
super.onPreExecute();
}
// -- called as soon as doInBackground method completes
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
m_loaded = true; //your list is done loading
}
}
Related
I want to access and populate a ListView from seprate thread... But its object has not its scope in new thread... What is the solution for it?
ListView FilesListView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FilesListView= (ListView)findViewById(R.id.remoteFilesListView);
new Thread ( new Runnable() {
#Override
public void run() {
// fetch data from server…
String xmlFormServer = Worker.getXmlresponse();
Log.d("Response from Serever", xmlFormServer;
// FilesListView object of Listview is not accessable in this thread to populate data…
}
}).start();
}
You cannot populate populate ListView/RecyclerView or any other view or component on another thread.
That has to be on the MainThread(UIThread).
Checkout this link for more info about UIThread in android and how its works.
What is the Android UiThread (UI thread)
However, you can put the logic or IO operation on different thread asynchronously and then you can put populate the result on UI thread.
One approach would be to use AsyncTask.
https://developer.android.com/reference/android/os/AsyncTask.html
Something like this..
new AsyncTask<Void, Void, String >() {
// Runs on Worker thread
#Override
protected String doInBackground(Void... params) {
String xmlFormServer = Worker.getXmlresponse();
return xmlFormServer;
}
// Runs on Ui thread
#Override
protected void onPostExecute(String data) {
if(data != null){
adapter.setDate(data);
adapter.notifyDataSetChanged();
}
}
}.execute();
You should to implement adapter for your ListView
Try to follow pattern below
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FilesListView = (ListView) findViewById(R.id.remoteFilesListView);
adapter = new SimpleAdapter(/*...*/); // implement your custom adapter or use native
// 1. set adapter into FileListView
FilesListView.setAdapter(adapter);
new AsyncTask<Void, Void, List>() {
#Override
protected List doInBackground(Void... params) {
String xmlFormServer = Worker.getXmlresponse();
Log.d("Response from Serever", xmlFormServer;
// 2. READ YOUR DATA HERE
List result = null; //
// 3. send result to ui thread
return result;
}
#Override
protected void onPostExecute(List list) {
// the method executes on ui thread
// 4. put your data into adapter
// you should implement the method or create native adapter
adapter.setDate(list);
// 5. refresh list - only UI THREAD can do this
adapter.notifyDataSetChanged();
super.onPostExecute(list);
}
}.execute();
}
I know this Question asked so many times but i cant find mistake
I was checked all things what is am doing wrong but I cant find what I did mistake.
I am using back4App for back end.
I am just retrieve data from that and just want to show in my listview.
I was set log to check data which i am retrieving so it show arraylist size perfect in doInbackground method. but when I return it to postexecute and there i am also check size of it.but it show 0 there.
dont know what mistake i was did.
My code is below:
public class HomeActivity extends AppCompatActivity {
ProgressDialog mProgressDialog;
ArrayList<String> items;
ListView listView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
listView = (ListView) findViewById(R.id.listview);
items=new ArrayList<>();
new GetServices().execute();
}
private class GetServices extends AsyncTask<Void, Void, ArrayList<String>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// Create a progressdialog
mProgressDialog = new ProgressDialog(HomeActivity.this);
// Set progressdialog title
mProgressDialog.setTitle("Parse.com Simple ListView Tutorial");
// Set progressdialog message
mProgressDialog.setMessage("Loading...");
mProgressDialog.setIndeterminate(false);
// Show progressdialog
mProgressDialog.show();
}
#Override
protected ArrayList<String> doInBackground(Void... params) {
// Locate the class table named "services" in Parse.com
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("services");
query.orderByAscending("updatedAt");
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
items.clear();
Log.i("TAG", "datasize=>" + objects.size());
List<String> temp = new ArrayList<String>();
for (ParseObject services : objects) {
temp.clear();
items.add(services.getString("serviceTitle"));
temp = services.getList("serviceDetails");
items.add(Util.getHtmlText(temp));
}
Log.i("TAG", "itemsize=>" + items.size());
} else {
e.printStackTrace();
}
}
});
return items;
}
#Override
protected void onPostExecute(ArrayList<String> strings) {
super.onPostExecute(strings);
Log.i("TAG", "resultsize=>" + strings.size());
for (int i = 0; i < strings.size(); i++) {
Log.i("TAG", "==>" + strings.get(i));
}
// Pass the results into an ArrayAdapter
CustomServiceListAdapter adapter = new CustomServiceListAdapter(HomeActivity.this, R.layout.custom_fragment_service_list_item, strings);
// Binds the Adapter to the ListView
listView.setAdapter(adapter);
// Close the progressdialog
mProgressDialog.dismiss();
// Capture button clicks on ListView items
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Send single item click data to SingleItemView Class
Intent i = new Intent(HomeActivity.this,
DetailsActivity.class);
// Pass data "name" followed by the position
i.putExtra("data", items.get(position));
// Open SingleItemView.java Activity
startActivity(i);
}
});
}
}}
My Logcat show below:
05-20 13:10:28.661 19099-19099/com.example.apexweb I/TAG: resultsize=>0
05-20 13:10:29.409 19099-19099/com.example.apexweb I/TAG: datasize=>4
05-20 13:10:29.410 19099-19099/com.example.apexweb I/TAG: itemsize=>8
Accordingly to the documentation findInBackground retrieves a list of ParseObjects that satisfy this query from the source in a background thread, and gets you the results in the callback. doInBackground does not wait until the callback with the results is called. That's why in onPostExecute the size is still 0. Since doInBackground runs already on a different thread, a second level of asynchronously is not necessary. You can call directly find(). Alternatively you can get rid of the AsyncTask and run findInBackground with the callback from the UI Thread
I've checked other answers regarding the asynchronous updates of ListFragments and have figured out that the most common problem for the notifyDataSetChanged() method not working is, that developers tend to overwrite the initial adapter source, causing the adapter to lose reference and hence not updating the view. I have a ListFragment that I want to update asynchronously. What I've decided to do, is parse the list data in an AsyncTask and finally update the fragment in onPostExecute(). I want the UI to be as fluid as possible, so I want to avoid doing heavy processing on the main thread. I call the AsyncTask after the views are created to avoid null pointers.
public class CategoryFragment extends ListFragment {
ArrayList<Category> categoriesArray = new ArrayList<Category>();
CategoryAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_category, container, false);
adapter = new CategoryAdapter(getActivity(), R.layout.category_list_item, categoriesArray);
setListAdapter(adapter);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
new UpdateUITask().execute(categories);
}
...
// The async task to update the UI.
class UpdateUITask extends AsyncTask<String, String, ArrayList<Category>>{
#Override
protected ArrayList<Category> doInBackground(String... input) {
// Do some data processing, to fill the categoriesArray.
// categoriesArray.add(...); -- loop
return categoriesArray;
}
#Override
protected void onPostExecute(ArrayList<Category> result) {
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
}
The refresh method fires, but produces no result. What else am I missing here? Should I pick a completely different approach?
Its looking like your instance of categoriesArray is getting lost. adapter.notifyDataSetChanged(); doesn't work only in case when your refrence of the listArray which you just passed to the adapter has been lost or changed. So, I would reccomend you to please make sure about this.
Also if you are going to populate your custom array then, use onProgressUpdate() method of the AsyncTask. It will reduce the loading time too.
You can do this like this:
class UpdateUITask extends AsyncTask<String, Category, ArrayList<Category>>
{
#Override
protected ArrayList<Category> doInBackground(String... input)
{
// Do some data processing, to fill the categoriesArray.
// and get the category objects one by one and call
//publishprogress till data is there
publishProgress(Category);
// and finallly just return somthing to get in onpostexecute
}
#Override
protected void onProgressUpdate(Category... values)
{
// TODO Auto-generated method stub
super.onProgressUpdate(values);
categoriesArray.add(...);
adapter.notifyDataSetChanged();
}
#Override
protected void onPostExecute(ArrayList<Category> result)
{
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
use below code for your AsyncTask, but please make sure again you are changing in the original categoryList.
private class UpdateUITask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Do some data processing, to fill the categoriesArray.
return null;
}
#Override
protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
}
}
and just run this task with passing any argument.
new UpdateUITask().execute();
I have an Activity that extends a ListActivity , in onCreate method i set the contentView and call an AsyncTask to load data and the list get filled as I want, the problem is when I check for example the detail activity and want go back the the listView: I get the onCreate method executed, data are loaded again and the listView is scrolled to the top loosing my previous position.
What I want to achieve is something like google gmail app for example: the list view get loaded once and the scroll position is saved.
I've looked so much over here and I tried many solutions but none is working.
What is the best way to achieve this scenario?
my activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
new LoadEvents().execute();
}
my asyncTask :
class LoadEvents extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Home.this);
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
protected String doInBackground(String... args) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
JSONObject json = jParser.makeHttpRequest(AgendaIConstantes.URL_EVENTS, "GET", params);
......Processing
eventList.add(map);
}
} else {
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
pDialog.dismiss();
runOnUiThread(new Runnable() {
public void run() {
ListAdapter adapterRes = getListAdapter();
if(adapterRes == null){
ListAdapter adapter = new SimpleAdapter(Home.this, eventList,R.layout.list_item,
new String[] { .....},
new int[] { .....});
setListAdapter(adapter);
}
}
});
}
Just do not execute your loading task every call of onCreate method. You can use either boolean flag in your activity or save state using onSaveInstanceState.
By the way, calling getListAdapter in onPostExecute is not very ok method and your null checking shows it.
I have created a custom adapter that inflates rows in a fragment. I would like to know how to put this in a thread.
In my fragment I have:
context = getActivity().getApplicationContext();
ListAdapter adapter = new NotesAdapter(courseId, context);
setListAdapter(adapter);
Every thing works this way but I have tried to put this in all four ways (AsyncTask, Java thread...) that Android offers for multithreading but the adapter won`t start that way. It just shows blank screen.
Can anyone help me how to put this in a separate thread?
For your reference,
public class SampleTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Do your Background process Eg.. some Parsing whatever it's, then paste you adapter initialization code
// Initialize your adapter as global
CustomAdapter sampleAdapter = new CustomAdapter(CurrentActivity.this,
R.id.ImageView01, <Your Arraylist/Array>);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Here you set your adapter
listView.setAdapter(sampleAdapter);
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
}