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();
}
}
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
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.
I have 3 Tabs like in the android development tutorial
Now what I want to do is very simple I use Fragments on each page. I want to show different content from a rss feed on each page.
The problem is when I go to the next tab it runs AsyncTask (which is in onCreateView) of the previous Fragment.
So you start on Page 1 it loads the content fine. Then when you go to Page 2 is runs the onCreateView of the Fragment of Page 1 again. And obviously gives an NullException. The point is it should not be running AsyncTask of Page 1 at all at that Page 2.
I don't think there is any example code needed if so tell me which part you need to see. Then I will edit my question.
AsyncTask inside a ListFragment :
public class MyAsyncTask extends AsyncTask<List<String>, Void, List<String>>
{
// List of messages of the rss feed
private List<Message> messages;
private volatile boolean running = true;
#SuppressWarnings("unused")
private WeakReference<NieuwsSectionFragment> fragmentWeakRef;
private MyAsyncTask(NieuwsSectionFragment fragment)
{
this.fragmentWeakRef = new WeakReference<NieuwsSectionFragment>(fragment);
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
mProgress.show();
// progress.setVisibility(View.VISIBLE); //<< set here
}
#Override
protected void onCancelled()
{
Log.w("onCancelled", "now cancelled");
running = false;
}
#Override
protected List<String> doInBackground(List<String>... urls)
{
FeedParser parser = FeedParserFactory.getParser();
messages = parser.parse();
List<String> titles = new ArrayList<String>(messages.size());
for (Message msg : messages)
{
titles.add(msg.getTitle());
// Log.w("doInBackground", msg.getTitle());
}
return titles;
}
#Override
protected void onPostExecute(List<String> result)
{
super.onPostExecute(result);
mProgress.dismiss();
if (result != null)
{
PostData data = null;
listData = new PostData[result.size()];
for (int i = 0; i < result.size(); i++)
{
data = new PostData();
data.postTitle = result.get(i);
data.postThumbUrl = "http://igo.nl/foto/app_thumb/28991-Taxi-vast-na-poging-tot-nemen-van-sluiproute.jpg";
listData[i] = data;
Log.w("onPostExecute", "" + listData[i].postTitle);
}
adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, listData);
setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
}
It's called inside a method and that method is executed inside the onCreateView of the ListFragment :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
startNewAsyncTask();
View rootView = inflater.inflate(R.layout.fragment_section_nieuws, container, false);
return rootView;
}
#SuppressWarnings("unchecked")
public void startNewAsyncTask()
{
MyAsyncTask asyncTask = new MyAsyncTask(this);
this.asyncTaskWeakRef = new WeakReference<MyAsyncTask>(asyncTask);
asyncTask.execute();
}
The LogCat :
Try using isAdded() before onPostExecute(). isAdded() returns true if the fragment is currently added to its activity.
http://developer.android.com/reference/android/app/Fragment.html#isAdded()
#Override
protected void postExecute(){
if(isAdded()){
//perform Display Changes
}
}
Move your
startNewAsyncTask();
to onActivityCreated()
I'm assuming your using FragmentPagerAdapter with your ViewPager.
To enable smooth animations, the ViewPager by default keeps the current fragment and the two neighbors in resumed state. So onCreateView is not the best place to start the AsyncTask.
Instead you need to create a custom listener interface. The fragments in the ViewPager should implement it, and call the new interface from the ViewPager's OnPageChangeListener.
Check out my answer to this question or you can read the whole tutorial here.
You're getting that exception because you're calling getActivity() too early. You should do it after onActivityCreated() (see this diagram)
Executing of onCreateView() in background is fine and actually is default behaviour. The thing is, ViewPager is optimised to load a content of neighbour non-visible pages in background to improve UX. You can do this:
mViewPager.setOffscreenPageLimit(2); (default value is 1) to load all 3 pages at once (1 is loading as currently visible and other 2 as optimisation). Or set it to 0 to disable this behaviour, but it's not the best idea.
In general, you should cash your loaded data and do not load it again by making your fragment's lifecycle methods as light as possible. Page limit of 2 is fine for 3 pages, but if you'll have for example 10 pages, limit of 9 is too much.
If I've understood your question right, I think you need unique content with each Fragment right?
Try using the varible arguments of the execute method. For example:
yourTask.execute(<some-unique-URL>, parameter, one-more-parameter);
In this way you can pass a unique URL per fragment form which you can get your content.
I feel you already have this. The doInBackground method has the List of URLs. You just need to pass that information in the execute method and utilize it in doInBackground.
Hope this helps!
It is normal that it runs the AsyncTask from the adjacent Fragments, since the ViewPager + PagerAdapter combo, works loading the current, previous and next Fragment.
You should focus the problem not to stop AsyncTask from running, but to let it run w/o throwing a NullPointerException.
The following should be called inside onCreateView()
adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, myList);
setListAdapter(adapter);
And then, onPostExecute()
myList.clear();
myList.addAll(listData);
adapter.notifyDataSetChanged();
The ViewPager will create views for fragments in adjacent pages and destroy views for fragments which are not adjacent to current page. Thus, the onCreateView of page 1 will get called if you navigate from page1->page2->page3->page2. You can have the viewpager keep more pages in memory by using ViewPager.setOffscreenPageLimit.
The fragmentPagerAdapter retains the fragment objects. Only the views are destroyed. Thus, when viewpage recreates page1's view, the fragment object is the same. Hence, all the fields in the fragment will get retained.
As in most applications where there's no realtime data, it is not required not load the data every time the view of the fragment is created, you can store the data in the fragment object after loading. Then, before starting the AsyncTask in onCreateView/onActivityCreated, check if the data has been previously loaded or not.
class PageFragment {
private List<String> mData;
...
void onActivityCreated() {
if (data == null) { // OR if the data is expired
startAsyncTask();
} else {
updateViews();
}
}
void updateViews() {
// Display mData in views
}
class LoadDataTask extends AsyncTask<List<String>, ..., ...> {
...
void onPostExecute(List<String> result) {
PageFragment.this.mData = result;
PageFragment.this.updateViews();
}
}
I recommend that you use loaders for loading data for a fragment. For your purpose, you can configure a loader to load data only once.
This is a great tutorial on Loaders.
http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
In the tutorial, the loader is configured to return previous data immediately if available, and then fetch data in background and return it after fetching completes. Thus, the UI will get updated after fresh data gets downloaded but at the same time, it will show the previous data initially while the download happens.
You can use another activity - this activity will run asynctask and then move to your fragment related activity. In this way it should call only once.
In case you need to update Fragment UI using this AsyncTask then use a static method to call through AsyncTask.
I am currently developing an android app. The app has 2 activities. Activity 1 is a ListActivity. ListActivity calls a standard Activity. When the 2nd activity is finished, I use the finish() method to close the activitity which returns to the ListActivity. I need the ListActivity to now refresh with the new dat
You should just call notifyDataSetChanged() on your adapter when you return back to your activity.
The API states:
void notifyDataSetChanged()
Notifies the attached View that the
underlying data has been changed and
it should refresh itself.
Have you packaged the data into and intent? For that matter did you startActivityForResult()? If so the you would take the data out of the intent by overriding the onActivityResult(...) method and add the data to what ever you need to do. Then like sahhhm said notifyDataSetChanged().
~Aedon
You need reload the info in the ListView...
#Override
protected void onResume() {
super.onResume();
mAdapter= new mYCustomAdapter(mActivity.this, valuesReloaded);
mList.setAdapter(mAdapter);
mAdapter.myMethodThatNotifyChange();
}
where:
valuesReloaded is the new Array with the new Values.
mAdapter is a Custom Adapter
myMethodThatNotifyChange is a method in the Class MyCustomAdapter where you must call notifyDataSetChanged() , ( in a custom adapter can only call from the class adapter)
class myCustomAdapter extends BaseAdapter{
...
...
myMethodThatNotifyChange{
notifyDataSetChanged();
}
...
...
}
Or finish the actual activity, and reload with a new intent
#Override
protected void onResume() {
super.onResume();
Intent mIntet = new Intent(mlistViewPersons.this , mlistViewPersons.class)
startActivity(mIntent);
finish();
}