2016-06-03 5 views
0

ForecastFragment.javaSunshine Project: Daten werden nach dem Aktualisieren der App nicht aktualisiert?

// Created by vgangwar7 on 31/05/16. 
public class ForecastFragment extends Fragment { 
public final class BuildConfig { 
    public static final String OPEN_WEATHER_MAP_API_KEY = "1912b14c788b31e4f1ae441a0ceefb18"; 
} 

private ArrayAdapter<String> forecastAdapter; 
public ForecastFragment(){} 

public void onCreate(Bundle savedInstanceState){ 
    super.onCreate(savedInstanceState); 
    // Add this line in order for this fragment to handle menu events 
    setHasOptionsMenu(true); 
    updateWeather(); 
} 

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    inflater.inflate(R.menu.forecastfragment, menu); 
} 

public boolean onOptionsItemSelected(MenuItem menuItem) { 
    int id = menuItem.getItemId(); 

    if (id == R.id.action_refresh) { 
     FetchWeatherTask weatherTask = new FetchWeatherTask(); 
     weatherTask.execute("110085"); 
     return true; 
    } 

    return super.onOptionsItemSelected(menuItem); 
} 

private void updateWeather() { 
    FetchWeatherTask weatherTask = new FetchWeatherTask(); 
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); 
    String location = preferences.getString(getString(R.string.pref_location_key), 
      getString(R.string.pref_location_default)); 
    weatherTask.execute(location); 
} 

public void onStart() { 
    super.onStart(); 
    updateWeather(); 
} 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 
    // Inflate the layout for this fragment 
    String[] forecastArray = 
      {"Today - Sunny - 88/63", 
      "Tomorrow - Foggy - 70/40", 
      "Weds - Cloudy - 72/63", 
      "Thurs - Asteroids - 75/65", 
      "Fri - Heavy Rain - 65/56", 
      "Sat - HELP TRAPPED IN WEATHERSITUATION - 65/51", 
      "Sun - Sunny - 88/68"}; 

    List<String> weekForecast = new ArrayList<String>(Arrays.asList(forecastArray)); 

    forecastAdapter = new ArrayAdapter<>(getActivity(),R.layout.list_item_forecast, R.id.list_item_forecast_textview, weekForecast); 
    View rootView = inflater.inflate(R.layout.fragment_main, container, false); 
    ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast); 
    listView.setAdapter(forecastAdapter); 
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
     @Override 
     public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { 
      Toast.makeText(getActivity(), forecastAdapter.getItem(position), Toast.LENGTH_SHORT).show(); 
     } 
    }); 
    return rootView; 
} 

public class FetchWeatherTask extends AsyncTask<String, Void, String[]> { 

    private final String TAG = FetchWeatherTask.class.getSimpleName(); 

    String format = "json"; 
    String units = "metric"; 
    final int numDays = 7; 

    private String getReadableDateString(long time) { 
     // Because the API returns a unix timestamp (measured in seconds), 
     // it must be converted to milliseconds in order to be converted to valid date. 
     SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd"); 
     return shortenedDateFormat.format(time); 
    } 
    /** 
    * Take the String representing the complete forecast in JSON Format and 
    * pull out the data we need to construct the Strings needed for the wireframes. 
    * 
    * Fortunately parsing is easy: constructor takes the JSON string and converts it 
    * into an Object hierarchy for us. 
    */ 
    //Prepare the weather high/lows for presentation. 
    private String formatHighLows(double high, double low) { 
     // For presentation, assume the user doesn't care about tenths of a degree. 
     long roundedHigh = Math.round(high); 
     long roundedLow = Math.round(low); 

     String highLowStr = roundedHigh + "/" + roundedLow; 
     return highLowStr; 
    } 

    /** 
    * Take the String representing the complete forecast in JSON Format and 
    * pull out the data we need to construct the Strings needed for the wireframes. 
    * 
    * Fortunately parsing is easy: constructor takes the JSON string and converts it 
    * into an Object hierarchy for us. 
    */ 
    private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays) 
      throws JSONException { 

     // These are the names of the JSON objects that need to be extracted. 
     final String OWM_LIST = "list"; 
     final String OWM_WEATHER = "weather"; 
     final String OWM_TEMPERATURE = "temp"; 
     final String OWM_MAX = "max"; 
     final String OWM_MIN = "min"; 
     final String OWM_DESCRIPTION = "main"; 

     JSONObject forecastJson = new JSONObject(forecastJsonStr); 
     JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST); 


     String[] resultStrs = new String[numDays]; 
     for(int i = 0; i < weatherArray.length(); i++) { 
      // For now, using the format "Day, description, hi/low" 
      String day; 
      String description; 
      String highAndLow; 

      // Get the JSON object representing the day 
      JSONObject dayForecast = weatherArray.getJSONObject(i); 

      //create a Gregorian Calendar, which is in current date 
      GregorianCalendar gc = new GregorianCalendar(); 
      //add i dates to current date of calendar 
      gc.add(GregorianCalendar.DATE, i); 
      //get that date, format it, and "save" it on variable day 
      Date time = gc.getTime(); 
      SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd"); 
      day = shortenedDateFormat.format(time); 

      // description is in a child array called "weather", which is 1 element long. 
      JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0); 
      description = weatherObject.getString(OWM_DESCRIPTION); 

      // Temperatures are in a child object called "temp". Try not to name variables 
      // "temp" when working with temperature. It confuses everybody. 
      JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE); 
      double high = temperatureObject.getDouble(OWM_MAX); 
      double low = temperatureObject.getDouble(OWM_MIN); 

      highAndLow = formatHighLows(high, low); 
      resultStrs[i] = day + " - " + description + " - " + highAndLow; 
     } 

     for (String s : resultStrs) { 
      Log.v("FetchWeatherTask", "Forecast entry: " + s); 
     } 
     return resultStrs; 

    } 

    @Override 
    protected String[] doInBackground(String... params) { 

     if (params.length == 0) { 
      return null; 
     } 

     //These two needed to be declared outside try catch block 
     HttpURLConnection urlConnection = null; 
     BufferedReader reader = null; 

     String forecastJsonStr = null; 

      try { 
       // Construct the URL for the OpenWeatherMap query 
       // Possible parameters are avaiable at OWM's forecast API page, at 
       // http://openweathermap.org/API#forecast 
       final String FORECAST_BASE_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?"; 
       final String QUERY_PARAM = "q"; 
       final String FORMAT_PARAM = "mode"; 
       final String UNITS_PARAM = "units"; 
       final String DAYS_PARAM = "cnt"; 
       final String APPID_PARAM = "APPID"; 

       Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon() 
         .appendQueryParameter(QUERY_PARAM, params[0]) 
         .appendQueryParameter(FORMAT_PARAM, format) 
         .appendQueryParameter(UNITS_PARAM, units) 
         .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)) 
         .appendQueryParameter(APPID_PARAM, BuildConfig.OPEN_WEATHER_MAP_API_KEY) 
         .build(); 

       URL url = new URL(builtUri.toString()); 

       Log.v(TAG, "Built URI " + builtUri.toString()); 

       // Create the request to OpenWeatherMap, and open the connection 
       urlConnection = (HttpURLConnection) url.openConnection(); 

       // Create the request to OpenWeatherMap, and open the connection 
       urlConnection = (HttpURLConnection) url.openConnection(); 
       urlConnection.setRequestMethod("GET"); 
       urlConnection.connect(); 

       // Read the input stream into a String 
       InputStream inputStream = urlConnection.getInputStream(); 
       StringBuffer buffer = new StringBuffer(); 
       if (inputStream == null) { 
        // Nothing to do. 
        return null; 
       } 
       reader = new BufferedReader(new InputStreamReader(inputStream)); 

       String line; 
       while ((line = reader.readLine()) != null) { 
        // Since it's JSON, adding a newline isn't necessary (it won't affect parsing) 
        // But it does make debugging a *lot* easier if you print out the completed 
        // buffer for debugging. 
        buffer.append(line + "\n"); 
       } 

       if (buffer.length() == 0) { 
        // Stream was empty. No point in parsing. 
        return null; 
       } 
       forecastJsonStr = buffer.toString(); 
      } catch (IOException e) { 
       Log.e("ForecastFragment", "Error ", e); 
       // If the code didn't successfully get the weather data, there's no point in attemping 
       // to parse it. 
       return null; 
      } finally 
      { 
       if (urlConnection != null) { 
        urlConnection.disconnect(); 
       } 
       if (reader != null) { 
        try { 
         reader.close(); 
        } catch (final IOException e) { 
         Log.e("ForecastFragment", "Error closing stream", e); 
        } 
       } 
      } 
     try{ 
      getWeatherDataFromJson(forecastJsonStr, numDays); 
     }catch (JSONException e){ 
      Log.e(TAG ,e.getMessage(), e); 
      e.printStackTrace(); 
     } 

     return null; 

    } 

    @Override 
    public void onPostExecute(String[] result) { 
     if (result != null) { 
      forecastAdapter.clear(); 
      for (String dayForecastStr : result) { 
       forecastAdapter.addAll(dayForecastStr); 
      } 
      forecastAdapter.notifyDataSetChanged(); 
     } 
    } 

} 
} 

MainActivity.java

public class MainActivity extends AppCompatActivity 
{ 

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    if (savedInstanceState == null) { 


    getSupportFragmentManager().beginTransaction().add(R.id.container, new ForecastFragment()).commit(); 
    } 

} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.main, menu); 

    return true; 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    int id = item.getItemId(); 

    //noinspection SimplifiableIfStatement 
    if (id == R.id.action_settings) { 
     return true; 
    } 
    if (id == R.id.action_refresh) { 
     return true; 
    } 

    return super.onOptionsItemSelected(item); 
} 
} 

aber meine logcat, ich bin in der Lage, die abgerufenen Daten aus der API und es gibt keine Fehler zu sehen im Code. LogCat -

06-04 16: 11: 31,432 3.730-3.769/com.example.vgangwar7.sunshine V/FetchWeatherTask: Prognose Eintrag: So 05 Juni - Clear - 43/31 04.06 16.11: 31.432 3730-3769/com.beispiel.vgangwar7.sunshine V/FetchWeatherTask: Prognoseeintrag: Mo Jun 06 - Löschen - 40/26 06-04 16: 11: 31.432 3730-3769/com.beispiel.vgangwar7.sunshine V/FetchWeatherTask: Vorhersageeintrag: Di Jun 07 - Löschen - 39/28 06-04 16: 11: 31.432 3730-3769/com.beispiel.vgangwar7.sunshine V/FetchWeatherTask: Vorhersageeintrag: Mi Jun 08 - Löschen - 39/26 06-04 16: 11: 31.432 3730-3769/com.beispiel.vgangwar7.sunshine V/FetchWeatherTask: Vorhersageeintrag: Do Jun 09 - Löschen - 41/27 06-04 16: 11: 31.432 3730-3769/com .beispiel.vgangwar7.sunshine V/FetchWeatherTask: Prognose Eintrag: Fr 10. Juni - Regen - 39/26

+0

I Wetterdaten glauben holen ist async Aufgabe . Ist es möglich, dass Sie das Layout laden, bevor die Aufgabe tatsächlich abgeschlossen ist? Ist es möglich, .get() bei Ihrer Ausführung aufzurufen? Wenn ja, tue dies, das stellt sicher, dass es vollständig ist, bevor zur nächsten Ausführung gesprungen wird. Beispiel: weatherTask.execute ("110085"). get(); – Jimmy

+0

Was möchten Sie eigentlich in der App sehen? Ihre Frage besagt, dass die Daten nach der Aktualisierung nicht aktualisiert werden. In der doInBackground-Methode liest man jedoch einfach die Wetterdaten für die nächsten 7 Tage täglich - so wird es sowieso nicht viel ändern (außer dass man die App am nächsten Tag aktualisiert). – Christian

+0

@Jimmy Die gefälschten Daten, die ich verwendet habe, werden zuerst angezeigt. Also, nach dem Klicken auf die Schaltfläche Aktualisieren wechselt zu onOptionsItemSelected, wo es eine neue AsyncTask abrufen P.S - Ich habe die Logcat –

Antwort

0

versuchen außer Kraft setzen onStart Methode:

@Override 
public void onStart(){ 
    super.onStart(); 
    updateWeather(); 
} 

Grüße.

+0

Entschuldigung, Sir Nichts ist passiert, nachdem ich das getan habe. –

0

Wenn Sie eine asynchrone Aufgabe ausführen, müssen Sie sicherstellen, dass die Aufgabe abgeschlossen ist, bevor Sie das von der Aufgabe erneut durchgeführte Ergebnis verwenden. In Ihrem Fall, wenn die Aktualisierung ausgewählt ist, führen Sie die Aufgabe mit weatherTask.execute("110085"); aus. Nehmen wir an, es dauert 3 Sekunden, um die Aufgabe abzuschließen und das Ergebnis zurückzugeben. aber Ihre return Anweisung in der nächsten Zeile wird sofort ausgeführt und zwingt das Layout neu zu laden (sagen wir, es dauert hier 1 Sekunde zum Nachladen). Wenn Sie eine asynchrone Task ausführen, wird ein anderer Prozess zum Abrufen von Daten erstellt, und die ursprüngliche Ausführung wird sofort fortgesetzt, ohne dass die asynchrone Task abgeschlossen wird. Die Ausführung wird sofort in die nächste Zeile verschoben, ohne auf das Ergebnis der Task zu warten. In Ihrem Fall wird das Neuladen durchgeführt, bevor Ihre Aufgabe tatsächlich die Daten abgerufen hat. Beispiel: Ihr Bildschirm wird in der ersten Sekunde geladen, wenn Sie die Methode "Option ausgewählt" eingeben, aber erst nach weiteren zwei Sekunden erhalten Sie Wetterdaten von der asynchronen Aufgabe. Die aktualisierten Daten erscheinen also in logcat, aber nicht im Bildschirm, da die Aktivität die Daten beim Laden nicht empfangen hat. Sie müssen also sicherstellen, dass Sie Ihr Layout erst laden, nachdem Sie Ihre Daten erhalten haben. Sie können dies tun, indem Sie get() bei Ihrer Ausführung aufrufen.

In Methode onOptionsItemSelected von ForecastFragment Ändern

weatherTask.execute("110085");

zu

weatherTask.execute("110085").get();

Dies sollte die Magie für Sie tun.

+0

Ich versuchte diese Methode, aber immer noch gibt es keine Änderung @Jimmy :( –

+0

Vielen Dank, aber ich fand den Fehler. –

+0

@VivekGangwar Bitte posten Sie Ihre Lösung und markieren Sie sie als Antwort, so dass andere darauf verweisen können, wenn sie lande es hier. – Jimmy

0

Ich bin mir nicht sicher, wann Sie die Daten "auffrischen" möchten und wie geht es Ihnen, können Sie nur Ihre Strategie erklären, um die Daten zu aktualisieren, anstatt Ihren gesamten Code zu kopieren? Normalerweise sind die Schritte:

  1. auszuführen HTTP-Anfrage in anderen Thread
  2. Sobald die Anforderung bereit ist, die Antwort auf den Haupt-Thread
  3. im Hauptthread aktualisiert die Benutzeroberfläche mit den Daten übergeben (dies hängt davon ab, die Steuerelemente, die Sie in Ihrer Anwendung werden unter Verwendung der Daten zu zeigen.

ich denke, Sie über die Aktivitätszustände in Android gelesen haben. Wenn Sie nicht taten, tun Sie es. Dann werden Sie in der Lage sein onStart zu verwenden oder onResume abhängig von Ihren Bedürfnissen.

+0

Ich lerne gerade Android von Udacity. Ja, Sie haben Recht, ich habe diese Schritte beim Erstellen der App verwendet. Selbst wenn, ich onStart Funktion aus dem Code entfernen, wird es keine Änderung. –

0

Rufen Sie die updateWeather(); Funktion im OnOptionsItemSelected() Methode

public boolean onOptionsItemSelected(MenuItem menuItem) { 
int id = menuItem.getItemId(); 

    if (id == R.id.action_refresh) { 
     //FetchWeatherTask weatherTask = new FetchWeatherTask(); 
     //weatherTask.execute("110085"); 
     updateWeather(); 
     return true; 
    } 

    return super.onOptionsItemSelected(menuItem); 
} 

In Ihrem doInBackground return-Anweisung hinzufügen, wenn Sie die Wettervorhersage Strings aus der forecastJsonStr machen

@Override 
protected String[] doInBackground(String... params) { 
    .//your code 
    .//your code 
    .//your code 
    . 
    . 
    try{ 
     return getWeatherDataFromJson(forecastJsonStr, numDays);//add return here!! 
    }catch (JSONException e){ 
     Log.e(TAG ,e.getMessage(), e); 
     e.printStackTrace(); 
    } 

    return null; 
} 
+0

Ich habe es bereits versucht dies ist aber nichts passiert! @muddasir –

+0

ich habe meine antwort aktualisiert bitte sehen sie die aktualisierte an. –