Reputation: 45
I'm learning to use REST API's by making an app that uses yahoo weather to display today's weather. I made an AsyncTask, a custom adapter and a WeatherData class that I call in my MainActivity. I'm having a little trouble using this WeatherData class and the custom adapter though.
In the adapter I get an error when I try to call the JSON objects from my weather data class. It says that the non-static field 'weather' and 'date' cannot be referenced from a static context. I've tried to read up on static contexts but it really confuses me as to what it is and what it does.
The second problem is that my AsyncTask now also gives an error when I try to add the date and weather to my weatherdata ArrayList. It says WeatherData(JSONobject) in WeatherData cannot be applied to JSONObject, java.lang.String. I think it has something to do with the fact that I'm using weather and date as objects in the WeatherData class, and then try to use them as strings in my arraylist. I tried to fix it by simply changing them to strings in the WeatherData class, but then I get all sorts of new errors. Can someone help me with where I'm going wrong?
So, this is the part of my MainActivity where I call the data:
public void getData(View view) {
ClassAsyncTask task = new ClassAsyncTask(this);
task.execute(chosenloc);
}
public void setData(ArrayList<WeatherData> weatherdata) {
// Construct the data source
ArrayList<WeatherData> arrayOfWeather = new ArrayList<WeatherData>();
// Create the adapter to convert the array to views
CustomArrayAdapter adapter = new CustomArrayAdapter(this, arrayOfWeather);
// Attach the adapter to a Listview
ListView listView = (ListView) findViewById(R.id.weatherListView);
listView.setAdapter(adapter);
}
This is my custom adapter:
public class CustomArrayAdapter extends ArrayAdapter<WeatherData> {
public CustomArrayAdapter(Context context, ArrayList<WeatherData> weatherdata){
super(context, 0, weatherdata);
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
// Get the data item for this position
WeatherData weather = getItem(position);
// Check if an exising view is being reused, otherwise inflate the view
if (convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.custom_array_layout, parent, false);
}
// Lookup view for data population
TextView date = (TextView) convertView.findViewById(R.id.date);
TextView weather = (TextView) convertView.findViewById(R.id.weather);
// Populate the data into the template view using the data object
date.setText(WeatherData.date);
weather.setText(WeatherData.weather);
// Return the completed view to render on screen
return convertView;
}
}
My WeatherData class:
public class WeatherData {
// Fields
public JSONObject date;
public String weather;
// Constructor
public WeatherData(JSONObject object) {
try {
JSONArray dates = object.getJSONArray("date");
JSONArray weather = object.getJSONArray("text");
} catch (JSONException e) {
e.printStackTrace();
}
}
// Method to convert an array of JSON objects into a list of objects
public static ArrayList<WeatherData> fromJson(JSONArray jsonObjects){
ArrayList<WeatherData> weather = new ArrayList<WeatherData>();
for (int i = 0; i < jsonObjects.length(); i++) {
try {
weather.add(new WeatherData(jsonObjects.getJSONObject(i)));
} catch (JSONException e) {
e.printStackTrace();
}
}
return weather;
}
}
And finally, the part of my AsyncTask where I parse the JSON:
protected void onPostExecute(String result){
super.onPostExecute(result);
// Alert user if nothing was found
if(result.length() == 0){
Toast.makeText(context, "Nothing was found", Toast.LENGTH_SHORT).show();
}
else {
// Parse JSON
ArrayList<WeatherData> weatherdata = new ArrayList<>();
try {
JSONObject respObj = new JSONObject(result);
JSONObject forecastObj = respObj.getJSONObject("forecast");
JSONArray dates = forecastObj.getJSONArray("date");
JSONArray weatherArray = forecastObj.getJSONArray("text");
for (int i = 0; i<dates.length(); i++){
JSONObject date = dates.getJSONObject(i);
String weather = date.getString("text");
weatherdata.add(new WeatherData(date, weather));
}
} catch (JSONException e) {
e.printStackTrace();
}
// Update MainActivity
this.activity.setData(weatherdata);
}
}
Upvotes: 1
Views: 795
Reputation: 39846
you have quite a few semantic errors, it's very important to understand the differences and nuances of static and non-static, but to fix your code it's just a matter of paying attention to the variables that you're using where. I am not gonna read through Yahoo weather API for you, so some stuff I'm showing here in a generic way.
fixed code follows:
this should be your getView
// Get the data item for this position
WeatherData weather = getItem(position);
// Check if an exising view is being reused, otherwise inflate the view
if (convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.custom_array_layout, parent, false);
}
// CHANGED HERE the textView "weather` had the same name of the "WeatherData" defined a few lines above
// Lookup view for data population
TextView textDate = (TextView) convertView.findViewById(R.id.date);
TextView textWeather = (TextView) convertView.findViewById(R.id.weather);
// CHANGED HERE to properly call the textView methods and to read the fields from the instance of "WeatherData"
// Populate the data into the template view using the data object
textDate.setText(weather.date);
textWeather.setText(weather.weather);
// Return the completed view to render on screen
return convertView;
also your constructor of "WeatherData" is illogical and will lead to NPE. I don't know what you're trying to achieve in there, but when you write JSONArray dates = object...
that information you extracted is being added to this JsonArray
and then it's thrown away. The field public JSONObject date
or public String weather
are never initialized and does not contain any useful information. what you probably want to do is something like that:
// Fields
public String val1;
public String val2;
public String val3;
... any value necessary
// Constructor
public WeatherData(JSONObject object) {
try {
val1 = object.optString("val1");
val2 = object.optString("val2");
val3 = object.optString("val3");
... parse here EVERYTHING from the object
} catch (JSONException e) {
e.printStackTrace();
}
}
also, on your postExecute
you're trying to create new WeatherData
passing two values to it, like this new WeatherData(date, weather)
. Again, read the WeatherData
constructor and it only uses 1 value, that is JsonObject object
.
Last is a performance one, you should not return string
to your onPostExecute. You should do an AsyncTask that returns ArrayList<WeatherData> result
. Then all the parsing code should be in the background
execution.
edit: example asyncTask
public class MyAsyncTask extends AsyncTask<String, Integer, List<WeatherObject>>{
@Override protected List<WeatherObject> doInBackground(String... params) {
// here you call the API and parse the results
// return ArrayList<WeatherObject> if successful, or `null` if failed
return null;
}
@Override protected void onPostExecute(List<WeatherObject> weatherObjectList) {
// here you simply check for null and use the List
if(weatherObjectList == null){
// fail to load data
} else {
// set data to adapter
}
}
}
Upvotes: 1