Android AsyncTask making sure all data has been loaded - java

Hi I'm trying to create an Asynctask that runs two functions and when they are done loading it then fills the UI with the data. at the moment for somereason it only fills the UI with data from one of the Functions and its which ever of the functions finishes loading first.
For Example
i have two functions LoadNewsFeed() and LoadResultsFeed() at the moment loadResultsFeed() shows the data and not anything from loadNews() however if i comment out LoadResultsFeed() the data from loadNews fills The UI but not from loadResultsFeed
is there a way i could set it to if that one is finished loading, load the other than perform the FillData() function?
heres what i have so far
public class PostTask extends AsyncTask {
#Override
protected Boolean doInBackground(Void... params) {
boolean result = false;
loadNewsFeed();
publishProgress("method1");
loadResultsFeed();
publishProgress("method2");
return result;
}
protected void onProgressUpdate(String... progress) {
StringBuilder str = new StringBuilder();
for (int i = 1; i < progress.length; i++) {
str.append(progress[i] + " ");
}
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
Log.v("BGThread", "begin fillin data");
FillData();
}
}
heres my FillData() Function
public void FillData(){
if (ChosenMethod.equals("Team")) {
arrayAdapter = new ArrayAdapter<String>(this, R.layout.single_item, newsList);
String[] mStrings = (String[]) imageList.toArray(new String[imageList.size()]);
String[] news = (String[]) newsList3.toArray(new String[newsList3.size()]);
arrayAdapter3 = new LazyAdapter(this, mStrings, news);
ListView list = getListView();
list.setTextFilterEnabled(true);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE );
View header = inflater.inflate( R.layout.homeheader, list, false);
View header2 = inflater.inflate( R.layout.homeheader2, list, false);
View header3 = inflater.inflate( R.layout.homeheader3, list, false);
//setListAdapter (arrayAdapter);
resultsView = LayoutInflater.from(getBaseContext()).inflate(R.layout.resultscell,
null);
TextView homeTeam = (TextView) resultsView.findViewById(R.id.HomeTeam);
homeTeam.setText(HomeTeam);
TextView awayTeam = (TextView) resultsView.findViewById(R.id.AwayTeam);
awayTeam.setText(AwayTeam);
TextView homeScore = (TextView) resultsView.findViewById(R.id.HomeScore);
homeScore.setText(HomeScore);
TextView awayScore = (TextView) resultsView.findViewById(R.id.AwayScore);
awayScore.setText(AwayScore);
TextView attendance = (TextView) resultsView.findViewById(R.id.Attendence);
attendance.setText("Att:" + Attendance);
TextView division = (TextView) resultsView.findViewById(R.id.Division);
division.setText(Division);
Log.v("BGThread", "Filled results");
adapter = new MergeAdapter();
adapter.addView(header);
adapter.addAdapter(arrayAdapter);
adapter.addView(header2);
adapter.addView(resultsView);
adapter.addView(header3);
adapter.addAdapter(arrayAdapter3);
setListAdapter(adapter);
Log.v("BGThread", "Filled Merge Adapter Team");
} else {
arrayAdapter = new ArrayAdapter<String>(this, R.layout.single_item, newsList);
arrayAdapter2 = new ArrayAdapter<String>(this, R.layout.single_item, newsList2);
//arrayAdapter3 = new ArrayAdapter(this, R.layout.complex_item, newsList3);
String[] mStrings = (String[]) imageList.toArray(new String[imageList.size()]);
String[] news = (String[]) newsList3.toArray(new String[newsList3.size()]);
arrayAdapter3 = new LazyAdapter(this, mStrings, news);
ListView list = getListView();
list.setTextFilterEnabled(true);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE );
View header3 = inflater.inflate( R.layout.homeheader3, list, false);
//setListAdapter (arrayAdapter);
adapter = new MergeAdapter();
adapter.addView(header3);
adapter.addAdapter(arrayAdapter3);
setListAdapter(adapter);
Log.v("BGThread", "Filled Merge Adapter League");
}
}

A quick and easy way is to add a boolean field to your class which is initially true:
private boolean bothFeedsLoaded = true;
Then toggle it's value in onPostExecute:
bothFeedsLoaded = !bothFieldsLoaded;
In your FillData method:
if (!bothFeedsLoaded){return;}
As bothFeedsLoaded is initially true, then the first callback to onPostExecute (after whichever feed loads first) will change it to false. Your fillData method will therefore return without doing anything. When the second feed finished loading, onPostEexcute will change it back to true and your fillData will do it's thing.
Alternatively (and perhaps a little easier to read in 6 months time) is to use an integer:
private int feedsLoaded = 0;
.....
feedsLoaded ++;
.....
if (feedsLoaded != 2) {return;}
There are better ways, e.g. creating separate classes for each loading task then synchronising both with a semaphore in their respective onPostExecute callbacks, but this should work.

If using AsyncTask is not crucial, i would suggest the use of Thread class along with Handler,
You can use join, the class which has those two methods, let it implement Runnable interface, then you can make an object of the class and pass it to the Thread constructor. Then use join on the thread, that means until and unless the thread dies it wont execute the below line, and just after the join, use the lines to place the data on the UI.
You can also you CountDownLatch from java.util.Concurrent package, which will wait for the specific work to be done, and then execute further.

Related

Android custom adapter and asyncTask not updating listView

I've searched all the posts I can find, and none seem to help with my situation. I have an android project that uses web services to pull down hourly weather data and populate a listView with the results.
The weird problem I'm having is that when I debug the project on my android phone, the main activity is blank and the listView isn't populated. If I run the project from android studio with my phone locked, and then unlock my phone the app opens on my phone with all of the listView properly formatted and populated.
I feel like it's a race condition issue between the asynctask and the adapter, but I can't seem to resolve it. I tried making my asyncTask an inner private class and calling notifyDataSetChanged on the adapter inside the onPostExecute method, but to no avail. I feel it must be something simple, but I'm relatively new to Android dev, so I'm stuck.
I have three classes that I'll post the pertinent code from
MainActivity.java (onCreate)
public class MainActivity extends ActionBarActivity {
ArrayList<Weather> w = new ArrayList<Weather>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DownloadWeatherTask myTask = new DownloadWeatherTask(w);
WeatherAdapter myAdapter = new WeatherAdapter(this,w);
ListView l = (ListView) findViewById(R.id.weatherList);
l.setAdapter(myAdapter);
myTask.execute();
}
}
WeatherAdapter.java
public class WeatherAdapter extends ArrayAdapter<Weather>{
public WeatherAdapter(Context context, ArrayList<Weather> weather) {
super(context, R.layout.item_weather, weather);
}
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Weather forecast = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_weather, parent, false);
}
// Lookup view for data population
TextView tvTime = (TextView) convertView.findViewById(R.id.listTime);
TextView tvDescr = (TextView) convertView.findViewById(R.id.listDescr);
TextView tvTemp = (TextView) convertView.findViewById(R.id.listTemp);
TextView tvHumid = (TextView) convertView.findViewById(R.id.listHumid);
ImageView ivWeather = (ImageView) convertView.findViewById(R.id.weatherImg);
// Populate the data into the template view using the data object
tvTime.setText(forecast.time);
tvDescr.setText(forecast.description);
tvTemp.setText(forecast.temperature+"°(F)");
tvHumid.setText(forecast.humidity+"% humidity");
ivWeather.setImageBitmap(forecast.weatherImg);
// Return the completed view to render on screen
return convertView;
}
}
DownloadWeatherTask.java
public class DownloadWeatherTask extends AsyncTask<Void,Void,Void>{
ArrayList<Weather> data;
public DownloadWeatherTask(ArrayList<Weather> a){
data = a;
}
public ArrayList<Weather> getData() {
return data;
}
protected Void doInBackground(Void...params) {
try {
String website = "http://api.wunderground.com/api/1111111111111/geolookup/q/autoip.json";
URL site = new URL(website);
HttpURLConnection weatherUnderground = (HttpURLConnection) site.openConnection();
weatherUnderground.connect();
JsonParser weatherParser = new com.google.gson.JsonParser();
JsonElement weatherJson = weatherParser.parse(new InputStreamReader((InputStream) weatherUnderground.getContent()));
JsonObject weatherObj = weatherJson.getAsJsonObject();
String zip = weatherObj.get("location").getAsJsonObject().get("zip").getAsString();
String city = weatherObj.get("location").getAsJsonObject().get("city").getAsString();
String state = weatherObj.get("location").getAsJsonObject().get("state").getAsString();
String hourly = "http://api.wunderground.com/api/111111111111/hourly/q/" + state + "/" + city + ".json";
URL hourlySite = new URL(hourly);
HttpURLConnection hourlyConnection = (HttpURLConnection) hourlySite.openConnection();
hourlyConnection.connect();
com.google.gson.JsonParser hourlyParser = new com.google.gson.JsonParser();
JsonElement hourlyWeatherJson = weatherParser.parse(new InputStreamReader((InputStream) hourlyConnection.getContent()));
JsonArray weatherArr = hourlyWeatherJson.getAsJsonObject().get("hourly_forecast").getAsJsonArray();
int l = weatherArr.size();
for (int i = 0; i < l; i++) {
String date = weatherArr.get(i).getAsJsonObject().get("FCTTIME").getAsJsonObject().get("pretty").getAsString();
String temp = weatherArr.get(i).getAsJsonObject().get("temp").getAsJsonObject().get("english").getAsString();
String condition = weatherArr.get(i).getAsJsonObject().get("condition").getAsString();
String humidity = weatherArr.get(i).getAsJsonObject().get("humidity").getAsString();
String iconUrl = weatherArr.get(i).getAsJsonObject().get("icon_url").getAsString();
Bitmap icon = getBitmapFromURL(iconUrl);
data.add(new Weather(date, condition, temp, humidity, icon));
}
} catch (IOException e) {
Log.e("Error: ",e.toString());
}
return null;
}
protected void onPostExecute(Void...params){
}
}
Below are links to my screenshots showing the app not populating the listView, and the app working properly when the program is run while the phone is initially locked.
Any help would be greatly appreciated!!
Thanks
In postExecute(), you need to update the adapter's List and then invoke its notifyDataSetChanged method. I suspect that you were forgetting to update the adapter's data.
The other option is to create a new adapter with the new data, and set the new adapter on the ListView.
I figured out what the issue was! I hadn't added #Override to my onPostExecute() method so it was never being called.
I added the notifyDataSetChanged to my onPostExecute as suggested, which worked once I added the #override to my method.

Android BaseAdapters & Multiple Listviews

I am currently creating a basic news aggregator app for Android, I have so far managed to create multiple HorizontalListViews derived from this: http://www.dev-smart.com/archives/34
I am parsing all data from live JSON objects and arrays.
The process goes something like this:
1) Start app
2) Grab a JSON file which lists all feeds to display
3) Parse feed titles and article links, add each to an array
4) Get number of feeds from array and create individual HorizontalListView for each. i.e. "Irish Times".
5) Apply BaseAdapter "mAdapter" to each HorizontalListView during creation.
My baseadapter is responsible for populating my HorizontalListViews by getting each title and thumbnail.
My problem is however that all my feeds seem to contain the same articles and thumbnails. Now I am only new to Android so I'm not 100% sure whats going wrong here. See screenshot below.
Do I need to create a new BaseAdaptor for each HorizontalListview or can I use the same one to populate all my listviews with unique data.
Here's some code to help explain what I mean:
1) OnCreate method to get JSON data, parse it, get number of feeds and create each HorizontalListView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listviewdemo);
//--------------------JSON PARSE DATA------------------
// Creating JSON Parser instance
JSONParser jParser = new JSONParser();
// getting JSON string from URL
String json = jParser.getJSONFromUrl(sourcesUrl);
//Parse feed titles and article list
getFeeds(json);
//Create Listviews
for(int i = 0; i < feedTitle.size()-1; i++){
//getArticleImage(i);
addHorzListView(i);
articleArrayCount++;//Used to mark feed count for adaptor to know which array position to look at and retrieve data from.
//Each array position i.e. [1] represents a HorizontalListview and its related articles
}
}
2) addHorzListView method, used to create HorizontalListView and apply adaptor
//Method used to dynamically add HorizontalListViews
public void addHorzListView(int count){
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View view = getLayoutInflater().inflate(R.layout.listview, mainLayout,false);
//Set lists header name
TextView header = (TextView) view.findViewById(R.id.header);
header.setText(feedTitle.get(count));
//Create individual listview
HorizontalListView listview = (HorizontalListView) view.findViewById(R.id.listviewReuse);
listview.setAdapter(mAdapter);
//add listview to array list
listviewList.add(listview);
mainLayout.addView(view, count);
}
3) Baseadaptor itself:
private BaseAdapter mAdapter = new BaseAdapter() {
private OnClickListener mOnButtonClicked = new OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(HorizontalListViewDemo.this);
builder.setMessage("hello from " + v);
builder.setPositiveButton("Cool", null);
builder.show();
}
};
#Override
public int getCount() {
return noOfArticles.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
//Each listview is populated with data here
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewitem, null);
TextView title = (TextView) retval.findViewById(R.id.title);
title.setText(getArticleTitle(position));
new DownloadImageTask((ImageView) retval.findViewById(R.id.ImageView01)) .execute(getArticleImage(position));
Button button = (Button) retval.findViewById(R.id.clickbutton);
button.setOnClickListener(mOnButtonClicked);
return retval;
}
};
The adapter mAdapter is currently displaying the articles from the last HorizontalListView that calls it.
Currently I am using the same BaseAdaptor for each ListView as I figured it populated the listview as soon as its called but i looks as though a BaseAdaptor can only be called once, I really dont know.
I want to dynamically populate feeds though without having to create a new Adaptor manually for each HorizontalListView.
Any help would be much appreciated.
So...you got the same info in 4 listview, right? In that case you only need oneAdapter populating 4 listview.
An adapter just provide the views which are visible in that moment to the listview (if it is implemented in the right way) so you can reuse the adapter if the info contained is the same.

getView() in ArrayAdapter not getting called from AsyncTask

In one of my android activity, I've a ListView lvFeedsList.
Each row element in the listView will contain 3 textViews - RSSFeedName, publishDate & feedLength
The contents of the feeds is retrived from a HTTPRsponse.
I'm fetching this response in an AsyncTask.
So, in the doInBackground(), I've send the HTTPRequest & received & parsed the response & prepared the ArrayList containing 3 above mentioned information.
Then inside the doInBackground() only, I'm creating the customized ArrayAdapter for forming the 3 TextViews in row element.
My intetions are to set this adapter on ListView in onPostExecute().
But, when I run the application, the ListView does not display anything.
I tried to debug & it seems like getView() in the ArrayAdapter class is not getting called. (But I'm not sure if this is the reason).
Here is the code, sorry for the length...it seemed necessary.
Activity Code:
public class GenericFeedsActivity extends Activity{
private ListView lvFeedsList;
private ArrayList<FeedsClass> feedList;
protected void onCreate(Bundle savedInstanceState) {
...
lvFeedsList = (ListView) findViewById(R.id.lvFeedsList);
lvFeedsList.setOnItemClickListener(this);
lvFeedsList.setEnabled(false);
...
new AsyncResponseHandler(this).execute();
}
class AsyncResponseHandler extends AsyncTask {
Context context;
FeedListAdapter adapter;
public AsyncResponseHandler(Context c) {
this.context = c;
}
#Override
protected Object doInBackground(Object... params) {
...
/*
* Sending HTTPRequest to a URL & getting list of feeds
* Saving this list of feeds in a ArrayList -feedList, containing elements of type FeedsClass (declared above)
* Below line parses the HTTPResponse XML & stores various information in feedList.
*/
feedList = utils.parseRssResponseXML(in); // Working fine, geeting elements
adapter = new FeedListAdapter(
GenericFeedsActivity.this, feedList);
in.close();
return null;
}
protected void onPostExecute(Object e) {
// Setting Arrayadapter
lvFeedsList.setAdapter(adapter);
lvFeedsList.setEnabled(true);
}
}
}
Adapter Code:
public class FeedListAdapter extends ArrayAdapter {
private Context context;
private ArrayList<FeedsClass> feedList;
public FeedListAdapter(Context c, ArrayList<FeedsClass> data) {
super(c, R.layout.rowlayout);
this.context = c;
this.feedList = data;
}
class ViewHolder {
TextView tvFeedName;
TextView tvFeedPubDate;
TextView tvFeedLength;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if (row == null) {
LayoutInflater inflator = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflator.inflate(R.layout.rowlayout, null);
holder = new ViewHolder();
holder.tvFeedName = (TextView) row.findViewById(R.id.tvFeedName);
holder.tvFeedPubDate = (TextView) row.findViewById(R.id.tvFeedPubDate);
holder.tvFeedLength = (TextView) row.findViewById(R.id.tvFeedLength);
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
}
// Getting values of feedName, publishDate & feedLength
String feedName = feedList.get(position).getTitle();
String feedDate = feedList.get(position).getPublishDate();
String feedLength = feedList.get(position).getStreamLength();
holder.tvFeedName.setText(feedName);
holder.tvFeedPubDate.setText(feedDate);
holder.tvFeedLength.setText(feedLength);
}
return row;
}
}
The issue is that you are subclassing ArrayAdapter. This doesn't work because ArrayAdapter internally thinks you do not have any elements in your data; it doesn't just magically know to look in the lvFeedsList variable because the data set it uses is internal.
Instead, in your constructor make sure to call this constructor instead:
Adapter code:
public FeedListAdapter(Context c, ArrayList<FeedsClass> data) {
super(c, R.layout.rowlayout, data); // add 'data'
this.context = c;
this.feedList = data;
}
Which will make everything work correctly.
adapter.notifyDataSetChanged()
could help at the end on of AsyncResponseHandler.onPostExecute(). If not - check whether ArrayList which hold data for adapter is empty or not.

Using Cursor with AsyncTask

I'm trying to download images for each artist that has music on my phone, then show these images in a GridView. I'm using the lastfm-java library that Last.fm recommends using. The method you call to fetch an artists image is getImageURL(ImageSize size), but before you do this, you need to tell it which artist you want to reference with a String parameter. So, in full it would be something like this:
#Override
protected String doInBackground(Object... arg0) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
return artist.getImageURL(ImageSize.EXTRALARGE);
}
Getting all the artists that are on my phone isn't a problem, you just reference MediaStore. You would do something like this:
private void getArtists() {
String[] projection = new String[] {
MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST,
};
String sortOrder = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;
Cursor c = getActivity().getContentResolver().query(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder);
if (c != null) {
int count = c.getCount();
if (count > 0) {
final int ARTIST_IDX = c.getColumnIndex(MediaStore.Audio.Artists.ARTIST);
for (int i = 0; i < count; i++) {
c.moveToPosition(i);
}
}
c.close();
c = null;
}
}
The Adapter for my GridView isn't anything special, it simply extends BaseAdapter.
Note AQuery is a library I'm using that helps cache and load a Bitmap from a URL.
public class GridViewAdapter extends BaseAdapter {
private final String[] imageURLs;
private final LayoutInflater mInflater;
private final Activity mActivity;
public GridViewAdapter(String[] urls, Activity activity) {
imageURLs = urls;
mActivity = activity;
mInflater = (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return imageURLs.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewholder = null;
// Inflate GridView items
if (convertView == null) {
convertView = mInflater.inflate(R.layout.gridview_items, null);
viewholder = new ViewHolder();
viewholder.mImage = (ImageView)convertView.findViewById(R.id.gridview_image);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder)convertView.getTag();
}
AQuery aq = new AQuery(convertView);
aq.id(viewholder.mImage).image(imageURLs[position], false, false, 0, 0, null, 0, 0.75f);
return convertView;
}
}
class ViewHolder {
public ImageView mImage;
}
So in full, my AsyncTask is as follows:
public class LastfmArtistGetImageURL extends AsyncTask<Object, Integer, String[]> implements
Constants {
private static final String tag = LastfmArtistGetImageURL.class.getSimpleName();
private GridViewAdapter mGridAdapter;
// Test
private final String[] imageIds = {
"http://userserve-ak.last.fm/serve/252/71875544.png",
"http://userserve-ak.last.fm/serve/252/6258507.jpg",
"http://userserve-ak.last.fm/serve/252/51274303.png",
"http://userserve-ak.last.fm/serve/252/58672183.png",
"http://userserve-ak.last.fm/serve/252/72029714.png",
"http://userserve-ak.last.fm/serve/252/17666215.jpg",
"http://userserve-ak.last.fm/serve/252/63247381.png",
"http://userserve-ak.last.fm/serve/252/33665463.jpg"
};
private final String artistOrMbid;
private final GridView mGridView;
private final Activity mActivity;
public LastfmArtistGetImageURL(String name, GridView gv, Activity activity) {
artistOrMbid = name;
mGridView = gv;
mActivity = activity;
}
#Override
protected String[] doInBackground(Object... arg0) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
Collection<String> col = new ArrayList<String>();
col.add(artist.getImageURL(ImageSize.EXTRALARGE));
return col.toArray(new String[0]);
}
#Override
protected void onPostExecute(String[] result) {
if (result != null)
mGridAdapter = new GridViewAdapter(imageIds, mActivity);
mGridView.setAdapter(mGridAdapter);
super.onPostExecute(result);
}
}
When I call my AsyncTask, I call it in my getArtists() method like this:
new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity())
.execute();
Problem
When I call this, all of the artists images download, but they download one after the other at position 0 of my GridViewAdapter. In other words, one image loads, then next, and so on all in the first position when I need them to be placed into each available position in the GridView. When I return my test String[] in my AsyncTask everything works like it should. All of the images are placed in order in each available space in the GridView.
Question
My question is, how do I return each artist image I download into my GridView correctly and why are the images currently only being loaded at the first position in my GridViewAdapter?
Edit - Shubhayu's answer
I moved setting my GridViewAdapter into my getArtists() method like so. This results in all the images being downloaded (As says LogCat), but only the last one being set in my GridView.
String[] test = new LastfmArtistGetImageURL(c.getString(ARTIST_IDX),
mGridView, getActivity()).execute().get();
mGridAdapter = new GridViewAdapter(test, getActivity());
mGridView.setAdapter(mGridAdapter);
smoak's answer
This results in only the last artist image (by the default order) being downloaded and applied in my GridView.
String[] test = {c.getString(ARTIST_IDX)};
new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();
Your AsyncTask looks like you are executing it each time for each Artist. Thus, your AsyncTask returns only one Artist's image and your GridView gets that Artists image, then you run the AsyncTask for the next Artist, GridView gets updated with new image and so on. What you need to do is modify your AsyncTask to take a String array of Artist names and loop over them in the doInBackground to get their image's.
// ... SNIPPED
public LastfmArtistGetImageURL(String[] names, GridView gv, Activity activity) {
artistsOrMbids = names;
mGridView = gv;
mActivity = activity;
}
#Override
protected String[] doInBackground(Object... arg0) {
Collection<String> col = new ArrayList<String>();
for (String nameOrMbid : this.artistsOrMbids) {
Artist artist = Artist.getInfo(artistOrMbid, LASTFM_API_KEY);
col.add(artist.getImageURL(ImageSize.EXTRALARGE));
}
return col.toArray(new String[0]);
}
// .... SNIPPED
And pass in all the artist names:
String[] artists = { "The Black Keys", "Rush", "The Allman Brothers" };
new LastfmArtistGetImageURL(artists, mGridView, getActivity()).execute();
here's what is happening, when you pass the test string it has a list of images and hence the gridview shows them properly. but when you use it to download an image for each artist, things go wrong.
Every time you call
new LastfmArtistGetImageURL(c.getString(ARTIST_IDX), mGridView, getActivity()).execute();
it runs the doInBackground(), completes it and then immediately calls the onPostExecute() where it creates a new adapter and passes your result which basically contains a single image of the single call.
So what u need to do is in your asynctask download all the images and then create a single adapter and pass all the images to it. That is not happening currently.
EDIT
If you see the AsyncTask, you will realize that everytime you call it, the string array returns only one image. So instead of returning a string array, return a string.
Next, I would suggest you use an ArrayList in your Adapter instead of a String array.
In your getArtists(), create an ArrayList and everytime you call
new LastfmArtistGetImageURL(test, mGridView, getActivity()).execute();
add the result to your ArrayList. Once you have looped through all the artists, your ArrayList will contain all the images.
Now set it to the Adapter. (You would have t change the adapter a bit if you change it from string to arraylist.)

android - SimpleCursorAdapter for a View

is it possible to create a View which is driven by a SimpleCursorAdapter. The content from this view is ever time a entry from DB.
The View (dataView) looks like:
txtData1
txtData2
txtData3
btnPrev btnNext
I read around and tryd to setup this behavior. Hope its make sens:
public class mActivity extends Activity {
public Context me = this;
public SimpleCursorAdapter mAdapter = null;
public Cursor mCursor = null;
private OnClickListener btnStart_onClick = new OnClickListener() {
public void onClick(View v) {
setContentView(R.layout.dataView);
mCursor = mDB.rawQuery("SELECT * FROM Data", null);
startManagingCursor(mCursor);
mAdapter = new SimpleCursorAdapter(
me,
R.layout.dataView,
mCursor,
new String[] {"Data1", "Data2", "Data3"},
new int[] {R.id.txtData1 , R.id.txtData2, R.id.txtData3});
mAdapter.setViewBinder(VIEW_BINDER);
mCursor.moveToFirst();
}
};
static final ViewBinder VIEW_BINDER = new ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex)
{
switch (view.getId())
{
case R.id.txtData1:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data1");
txt.setText(cursor.getString(index));
}
return true;
case R.id.txtData2:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data2");
txt.setText(cursor.getString(index));
}
return true;
case R.id.txtData3:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data3");
txt.setText(cursor.getString(index));
}
return true;
default:
return false;
}
}
};
}
When I run from the btnStart_onClick I dont get Data in my Textboxes :-(
Can somebody help? Can it work like this?
Next question: how can I use the Prev or Next Buttons? Possible this is the only thing I miss to "load" the first data...
EDIT: I extended my example with the global mCursor and the call to mCursor.moveToFirst()
On my app I also tested with the next / prev buttons and the function mCursor.moveToNext() and mCursor.moveToPrevious()
But its not change :-(
As far as I can tell, there are a lot of what I think are conceptual/organizational/syntactical problems with your code. First of all, an adapter is usually exploited by a view such as ListView or Spinner, that gets populated with the data retrieved by the adapter via the cursor (or whatever data structure is backing it). However, I don't see this pattern in your code, and I'm left wondering what use an adapter would have in your case.
Second, you perform a whole SELECT * query in your click listener, i.e. you retrieve all your 1000 records for each click on... well, on what, exactly? You define the click listener, but never set it onto anything - just as you define the adapter, but you don't bind it to anything. The code that sets up the adapter, with the database query and the binder should really be placed outside the listener.
Last, I believe you mocked variable names a bit before posting the code, because in the following snippet:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data1");
String txt = cursor.getString(index);
txt.setText(txt);
}
I could hardly see how the compiler is intended to distinguish the two txt variables on the last line of the if body.

Categories

Resources