Adding items to spinner dynamically in AsyncTask - android

I've followed some threads here on SO but I can't get this to work even though I feel like it's matching the examples and I don't know why.
I created a simple spinner.. and in my AsyncTaskRunner class I pull information from a REST API and then in the onPostExecute I try to add it to the Spinner.. but when I debug/run my app the Spinner contents is empty. When debugging the webChannelList is for sure not empty and has content
If I don't try to add anything dynamically and just add things manually in the onCreate method it works just fine.. but for realistic purposes I'm trying to do it dynamically.
SO was saying to pass in the MainActivity which i did.. but it's not working. Help!
SOLUTION: I had Debug.waitForDebugger(); in my doInBackground function... once I removed that it worked perfectly.
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/singularSpinner" />
I ommited stuff not relevant to what i'm asking
public class MainActivity extends AppCompatActivity {
// Global Variables
private List<String> webChannelList = new ArrayList<String>();
private Spinner spinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Load Singular Web Channels into GUI
AsyncTaskRunner runner = new AsyncTaskRunner(this);
runner.execute();
}
class AsyncTaskRunner extends AsyncTask<Void, Void, Void> {
MainActivity mainActivity;
#Override
protected Void doInBackground(Void... params) {
Debug.waitForDebugger(); //REMOVING THIS WORKS
webChannelList = StuffFromAPI();
return null;
}
#Override
protected void onPostExecute(Void result) {
if (!webChannelList.isEmpty()) {
spinner = (Spinner) findViewById(R.id.singularSpinner);
// Creating adapter for spinner
ArrayAdapter<String> dataAdapter =
new ArrayAdapter<String>(mainActivity, android.R.layout.simple_spinner_item, webChannelList);
// Drop down layout style - list view with radio button
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Attaching data adapter to spinner
spinner.setAdapter(dataAdapter);
}
}
}
}
EDIT: so I ommited the StuffFromAPI() function and just added stuff manually with webChannelList.add("hello"); for example and it ran perfectly from the StuffFromAPI() function .. strangely even though I just commented it out and added things in manually. Almost like it's caching? But now I can't replicate it.. so something is going on and i dont know what. I'm new to Android development maybe there's something very obvious that I'm missing. All I do is go to Build --> Run or Build --> Debug and i have a tablet attached
EDIT 2: is it possible that the onPostExecute function runs BEFORE it grabs the items from my API? which still doesn't make sense because I physically see the content in there when i debug but it still doesn't show in the GUI

Response to Edit2: Yes it is possible, it all depends on if your API calls are asynchronous. You should check the size of the array list that is returned. How are you fetching the data?
This is not your problem, but I'd suggest arranging your code a bit differently.
Create members for the ArrayAdapter and the response data list, webChannelList. Then in the onPostExecute, all you should need to do is call notify that the data has changed. This is a bit more flexible and easier to maintain down the road.
ArrayList<someType> webChannelList = new ArrayList<>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
spinner = (Spinner) findViewById(R.id.singularSpinner);
// Creating adapter for spinner
dataAdapter = new ArrayAdapter<String>(mainActivity, android.R.layout.simple_spinner_item, webChannelList);
// Drop down layout style - list view with radio button
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Attaching data adapter to spinner
spinner.setAdapter(dataAdapter);
// Load Singular Web Channels into GUI
AsyncTaskRunner runner = new AsyncTaskRunner(this);
runner.execute();
}
class AsyncTaskRunner extends AsyncTask<Void, Void, Void> {
MainActivity mainActivity;
#Override
protected Void doInBackground(Void... params) {
webChannelList = StuffFromAPI();
return null;
}
#Override
protected void onPostExecute(Void result) {
dataAdapter.notifyDataSetChanged();
}
}

Related

android notifyDataSetChanged not working if delete item from data item

I am developing an android application in which I am using one base adapter and displaying data in list form.Now what happened If I remove any object from list and if I call notifyDataSetChanged, its not working as expected. I tried this in following manner.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
shareDataList = getData();
shareListAdapter = new SharedHistoryListAdapter(this, shareDataList);
sharedList.setAdapter(shareListAdapter);
}
#Override
protected void onResume()
{
super.onResume();
// after deleting data it is coming inside onresume ...
shareDataList = getData();
shareListAdapter.notifyDataSetChanged();
}
I checked data size inside my activity and inside adapter as well. So in activity it is showing count as expected but inside adapter it is not showing updated count. Am I missing something? Need some help.
You cannot change the reference of the list after setting the list to the adapter. so remove the line
shareDataList = getData();
and replace with
shareDataList.clear();
shareDataList.addAll(getData());
and now invoke the shareListAdapter.notifyDataSetChanged();
I think, in getData() method gets every time new ArrayList object.
You should simply call
shareListAdapter.notifyDataSetChanged();
it will work perfectly

Using async Task to load an apps listView with images in Android

I have a fragment dialog containing a list of all the apps installed on the device.
Sometimes the loading takes some time and I would like to show a ProgressDialog while it loads.
I've tried the following code which didn't do any good :
#Override
public void onAttach(Activity activity) {
// Show progress dialog
showProgressDialog(activity);
super.onAttach(activity);
}
private void showProgressDialog(Activity activity) {
mProgressDialog = new ProgressDialog(activity);
mProgressDialog.setTitle(R.string.loading);
mProgressDialog.setMessage(getString(R.string.shared_options_wait_while_applist_loads));
mProgressDialog.show();
}
The onCreate loads the whole list and the app images, and then I use :
#Override
public void onStart() {
stopProgressDialog();
super.onStart();
}
private void stopProgressDialog() {
mProgressDialog.dismiss();
}
Now I'm thinking about loading the whole list in a async task, but I can't figure what should the async task do, it should probably load the list, but how can I wait for the list and get the list when it's ready ? (I believe something like a callback should be used?)
Thanks
You can watch dataset changes in your adapter by adding a DataSetObserver.
#Override
public void onCreate(Bundle savedInstanceSate){
super.onCreate(savedInstanceState);
setContentview(R.layout.main_view);
ListView myList = (ListView) findViewById(R.id.my_list);
MyDataTypeAdapter adapter = new MyDataTypeAdapter();
myList.setAdapter(adapter);
adapter.registerDataSetObserver( new DataSetObserver(){
#Override
public void onChange(){
//do stuff here
}
});
}
This way onChange will be loaded when your dataSet changes and you can download your image there.
However, I will better do an AsyncTask for each row in your adapter and download it's image independently. You could also use UniversalImageLoader library for this purpose.
you can try Android Bitmap Fun example
Android Image Demo : http://developer.android.com/training/displaying-bitmaps/index.html
This example with GridView, you can use the same adapter for ListView by changing view in listItem.

How to access ListFragment Adapter within a ViewPager

I have an app that downloads data which must be displayed within the fragments of a viewpager. I dont know how to call the ListFragment adaptername.notifyDataSetChanged() in the AsyncTask that does the data download.
For example say i am downloading the temperature forecast for the next seven days:
my app has the following structure:
MainActivity: Starts an AsyncTask to download the data in onCreate() and gives the user choice (button) of which day to look at. Clicking the button launches SecondActivity and passes the day index to the ViewPager (to set the current view).
SecondActivity: Contains a ViewPager that contains 7 of the same ListFragments (The list display the temperature over a period of 5 hours, so the list has 5 entries).
MyListFragment: when this loads it sets the adapter to display each temperature (If the data is downloaded) otherwise it sets the temperature to "loading..."
Now my problem is, if the user waits on the MainActivity until the data downloads they can then proceed to the ViewPager to see the ListFragment temperatures without problem. But if they try click a day and load the ViewPager before the download completes the fragments will forever just say "loading..."
I need a way that I can reload the adapter within the ListFragment from the onPostExecute() of my AsyncTask in MainActivity. To do this though i need to be able to actually access the ListFragment that the ViewPager is displaying. How do update the adapter onPostExecute()?
MainActivity:
protected void onCreate(Bundle savedInstanceState) {
...
new LoadData().execute();
}
protected class LoadData extends AsyncTask<String, Void, String> {
protected String doInBackground(String... params) {
//Download happens here
}
protected void onPostExecute(String result) {
//I need to tell the viewpager in SecondActivity to reload the ListFragment it is currently showing here
}
}
SecondActivity
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
private int[] temperatureArray;
public void onCreate(Bundle savedInstanceState) {
...
Intent intent = getIntent();
int[] defaultTemps = {0, 0, 0, 0, 0};
temperatureArray = getIntArrayExtra("temps", defaultTemps);
mPager = (ViewPager) findViewById(R.id.pagerID);
mPager.setCurrentItem(intent.getIntExtra("page", 0));
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
...
public Fragment getItem(int position) {
MyListFragment lf = new MyListFragment();
lf.setTemperatures(temperatureArray);
return lf;
}
}
I need a way to be able to refresh the current displayed fragment when i finish downloading in the AsyncTask.
I can suggest doing one of two things, not sure if these are best practices.
You can Have the AsyncTask send a broadcast with a unique action when it has finished loading the information. That would of course have to be done from OnPostExecute:
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (result != null) {
//_Do whatever action you normally do, like storing result to database.
//fire up the broadcast
Intent intent = new Intent(Home.ACTION_FEED_LOADING_FINISHED);
mContext.sendBroadcast(intent);
}
}
And then intercept that via a BroadcastReceiver on your Fragment's code.
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ACTION_FEED_LOADING_FINISHED)) {
//The AsyncTask Finished loading data
}
}
};
Once the event is received, since the BroadcastReceiver is in the Fragment, where the views are loaded, you can refresh your UI accordingly.
Or,
You can pass the View you want to refresh as a paremeter on your AsyncTask.
Imagine your AsyncTask class like this:
private static class getInternetStuffTask extends AsyncTask<String, Void, Void> {
Then you can create a custom constructor to receive the view as paremeter:
public getInternetStuffTask (final Context context, final ListView list) {
this.mContext = context;
this.mListView= list;
}
Then, during onPostExecute, when data is loaded, you can simply set the adapter to the ListView directly form the AsyncTask.
You're touching on two different problems. First, use a Service for downloading. Services are there for long running operations (like a download) that live outside an Activity's lifecycle.
Second, to communicate between Activities and Fragments you'll want to use an callback interface. The link provided is to the docs which do an excellent job of explaining and providing samples.

Android app crashes when defining and setting element properties

My app is crashing. What am I doing wrong?
I am using an AsyncTask in a class fetchsSchools.
public class fetchSchools extends AsyncTask<Void, Void, ArrayList<String>>{
#Override
protected ArrayList<String> doInBackground(Void... arg0) {
ArrayList<School> schools = new ArrayList<School>();
ArrayList<String> schoolNames = new ArrayList<String>();
... code omitted for conciseness...
return schoolNames;
}
In this class I have an onPost Execute, my code gets to this, if I remark out the cls2 lines my app runs:
public void onPostExecute(ArrayList<String> schoolNames) {
MainActivity cls2=new MainActivity();
cls2.updateSpinner(schoolNames);
cls2.switchScreens();
}
The above fires off these two outines back in MainActivity which crash the app:
public void updateSpinner(ArrayList<String> schoolNames) {
Spinner schoolSpinner = (Spinner)findViewById(R.id.school_spinner);
schoolSpinner.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, schoolNames));
}
public void switchScreens() {
ProgressBar progressBar1 = (ProgressBar)findViewById(R.id.progressBar1);
progressBar1.setVisibility(View.GONE);
TextView loading_label = (TextView)findViewById(R.id.loading_label);
loading_label.setVisibility(View.GONE);
}
Eclipse isn't showing any coding errors. Am I creating and acting on these variables correctly?
Though you have not given code for MainActivity and logcat. Seeing your code looks like MainActivity extends Activity. If so you cannot call
MainActivity cls2=new MainActivity();
MainActivity has to be initialized by android framework with appropriate context. Calling constructor yourself will not call any lifecycle methods of the Activity.
So all subsequent calls that use Context will fail, especially findViewByid
You need to do startActivity instead of what you are doing.
Edit:
Create a constructor in Asynctask pass MainActivity when instantiating and assign like below and remove new MainActivity line
public class fetchSchools extends AsyncTask<Void, Void, ArrayList<String>>{
MainActivity cls2;
fetchSchools(MainActivity activity){
cls2 = activity;
}
#Override
protected ArrayList<String> doInBackground(Void... arg0) {
ArrayList<School> schools = new ArrayList<School>();
ArrayList<String> schoolNames = new ArrayList<String>();
... code omitted for conciseness...
return schoolNames;
}
public void onPostExecute(ArrayList<String> schoolNames) {
cls2.updateSpinner(schoolNames);
cls2.switchScreens();
}
}
In MainActivity, you call
new fetchSchools(mMainActivity).execute(param);
If this is a seperate class from that your main activity, then you're trying to findViewById from where? You probably get NullPointerException on very first line of onPostExecute which is this one:
cls2.updateSpinner(schoolNames);
Because you try to find a view in this method. But you do not have any parent view which contains the view that you seek to find.
So i would suggest you to move this fetchSchools class as a private inner class of your mainActivity. so you'll be able to find those views globally and set them however you want.

notifyDataSetChanged() on my adapter does not update the listview, why?

I have a activity that extends listactivity, extended in this class i have a class that extends baseadapter.
now in my listactivity i have this onCreate
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new BuildingAdapter(this));
final ProgressDialog loading = new ProgressDialog(this);
loading.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loading.setTitle("Laddar");
loading.setMessage("Hämtar data från servern");
loading.show();
new AsyncTask<Void, Void, DataPacket.Data>()
{
#Override
protected DataPacket.Data doInBackground(Void... params){
return ServerConnection.getBuildings();
}
#Override
protected void onPostExecute(DataPacket.Data param) {
((MainenanceApplication)getApplication()).setDataStructure(param);
loading.dismiss();
//((BuildingAdapter)getListAdapter()).notifyDataSetChanged();
setListAdapter(new BuildingAdapter(BuildingSelectionActivity.this));
}
}.execute();
}
This works as it's supposed to, but my question is in onPostExecute I update the datastructure that the list adapter uses.
Why cant I just call notifyDataSetChanged ??
If I have that line the view does not update itself, but if I use the line under where I do setListAdapter, it all works.
If the adapter is already set, setting it again will not refresh the listview. Instead first check if the listview has a adapter and then call the appropriate method.
I think its not a very good idea to create a new instance of the adapter while setting the list view. Instead, create an object.
BuildingAdapter adapter = new BuildingAdapter(context);
if(getListView().getAdapter() == null){ //Adapter not set yet.
setListAdapter(adapter);
}
else{ //Already has an adapter
adapter.notifyDataSetChanged();
}
Have you tried using an AsyncTaskLoader instead of an AsyncTask for this. It's this kind of stuff that Loaders were exactly designed for. Note that even though Loaders weren't available until API-10 you can still easily access them via the android Support Pacakge from API-4 and up.
The only place you can update the UI is in onProgressUpdate(...);. From your doInBackground(...), call publishProgress(...).

Categories

Resources