UDACITY Sunshine Android应用程序显示多个数据值和多个刷新选项

时间:2016-06-22 23:55:34

标签: java android

我是Android的新手,我正在尝试使用UDACITY课程来构建阳光应用程序。 感谢几个stackoverflow帖子,我能够从崩溃中修复我的应用程序,然后使用API​​密钥从互联网上获取数据。 一切正常,但数据是重复的,有2个动作栏,因此有2个刷新按钮。 这是在我完成第2课之后。

我的forecastfragment.java类和MainActivity.java类的代码如下:     包com.example.hemant.sunshine.app;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* A placeholder fragment containing a simple view.
*/
public class ForecastFragment extends Fragment {
ArrayAdapter<String> mForecastAdapter;
public ForecastFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
    inflater.inflate(R.menu.forecastfragment,menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item){
    int id=item.getItemId();
    if(id==R.id.action_refresh){
        FetchWeatherTask weatherTask = new FetchWeatherTask();
        weatherTask.execute("47408");
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    new FetchWeatherTask().execute("47408");
    super.onActivityCreated(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    String[] forecastArray={
            "Mon 6/23 - Sunny - 31/17",
            "Tue 6/24 - Foggy - 21/8",
            "Wed 6/25 - Cloudy - 22/17",
            "Thurs 6/26 - Rainy - 18/11",
            "Fri 6/27 - Foggy - 21/10",
            "Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
            "Sun 6/29 - Sunny - 20/7"
    };
   List<String> weekForecast = new ArrayList<String>       
  (Arrays.asList(forecastArray));
    // Now that we have some dummy forecast data, create an ArrayAdapter.
    // The ArrayAdapter will take data from a source (like our dummy     
   forecast) and
    // use it to populate the ListView it's attached to.
    // Now that we have some dummy forecast data, create an ArrayAdapter.
    // The ArrayAdapter will take data from a source (like our dummy 
     forecast) and
    // use it to populate the ListView it's attached to.
    mForecastAdapter =
            new ArrayAdapter<String>(
                    getActivity(), // The current context (this activity)
                    R.layout.list_item_forecast, // The name of the layout  
  ID.
                    R.id.list_item_forecast_textview, // The ID of the  
 textview to populate.
                    weekForecast);
    View rootView= inflater.inflate(R.layout.fragment_main, container,  
 false);
    ListView listView = (ListView) rootView.findViewById(
            R.id.listview_forecast);
    listView.setAdapter(mForecastAdapter);

    return rootView;
    }
   public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {

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

    /* The date/time conversion code is going to be moved outside the  
 asynctask later,
    * so for convenience we're breaking it out into its own method now.
     */
            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.
                Date date = new Date(time * 1000);
   impleDateFormat format = new      SimpleDateFormat("EEE MMM dd");
                    return format.format(date).toString();
                }

                    /**
            * 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 

线框。          + *          + *幸运的是,解析很简单:构造函数接受JSON字符串并转换它          + *进入我们的对象层次结构。          + * /          private String [] getWeatherDataFromJson(String forecastJsonStr,int          NUMDAYS)                         抛出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_DATETIME = "dt";
                    final String OWM_DESCRIPTION = "main";

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

            // OWM returns daily forecasts based upon the local time of the 
           //city that is being
          // asked for, which means that we need to know the GMT offset to     
           //translate this data
                                           // properly.

           // Since this data is also sent in-order and the first day is 
          //always the
         // current day, we're going to take advantage of that to get a nice

          // normalized UTC date for all of our weather.

                   /* Time dayTime = new Time();
                    dayTime.setToNow();

    // we start at the day returned by local time. Otherwise this is a mess.
   int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), 
     dayTime.gmtoff);

                            // now we work exclusively in UTC
                                    dayTime = new Time();*/

                            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);

          // The date/time is returned as a long.  We need to convert that
     // into something human-readable, since most people won't read 
      //"1400356800" as
                                                    // "this saturday".
                 long dateTime= dayForecast.getLong(OWM_DATETIME);

                            day = getReadableDateString(dateTime);

  // 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(LOG_TAG, "Forecast entry: " + s);
                            }
                            return resultStrs;

                        }

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

 //If there's no zzip code, there's nothing to look up. Verify size of 
  //params.
        if (params.length==0){
            return null;
        }
        // These two need to be declared outside the try/catch
        // so that they can be closed in the finally block.
        HttpURLConnection urlConnection = null;
        BufferedReader reader = null;

        // Will contain the raw JSON response as a string.
        String forecastJsonStr = null;
        String format= "json";
        String units= "metric";
        int numDays= 7;

        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(LOG_TAG, "Built URI " + builtUri.toString());

    /* String baseUrl = 
     "http://api.openweathermap.org/data/2.5/forecast/daily?
     q=94043&mode=json&units=metric&cnt=7";
          String apiKey = "&APPID=" + BuildConfig.OPEN_WEATHER_MAP_API_KEY;
            URL url = new URL(baseUrl.concat(apiKey));*/

            // 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();
            Log.v(LOG_TAG, "Forecast JSON String:" +forecastJsonStr);
        } catch (IOException e) {
            Log.e(LOG_TAG, "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(LOG_TAG, "Error closing stream", e);
                }
            }
        }
        try {
            return getWeatherDataFromJson(forecastJsonStr, numDays);
        } catch (JSONException e) {
            Log.e(LOG_TAG, e.getMessage(), e);
            e.printStackTrace();
        }
        return null;
    }
    @Override
    protected void onPostExecute(String[] result) {
        if (result != null) {
            mForecastAdapter.clear();
            for(String dayForecastStr : result) {
                mForecastAdapter.add(dayForecastStr);
            }
            // Data has been returned from the server
            mForecastAdapter.notifyDataSetChanged();
         }
       }
      }
     }

MainActivity.java如下 - :      包com.example.hemant.sunshine.app;

 import android.os.Bundle;
 import android.support.design.widget.FloatingActionButton;
 import android.support.design.widget.Snackbar;
 import android.support.v7.app.ActionBar;
  import android.support.v7.app.ActionBarActivity;
  import android.support.v7.app.AppCompatActivity;
  import android.support.v7.widget.Toolbar;
  import android.view.View;
  import android.view.Menu;
  import android.view.MenuItem;

  public class MainActivity extends AppCompatActivity {

  @Override
  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;
    }

    return super.onOptionsItemSelected(item);
  }
  }

1 个答案:

答案 0 :(得分:0)

  1. 您通过静态和动态方式使用相同的片段。那就是frameLayout和activity_main.xml中的片段。您将通过FragmentTransaction在MainActivity中启动第一个fargment,如下所示。

        if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new ForecastFragment())
                .commit();
        }
    
  2. 另一个片段是在content_main.xml

    中静态创建的
     <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/fragment"
        android:name="com.sunshine.ForecastFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:layout="@layout/fragment_main" />
    

    因此,您正在启动2个片段并查看重复数据。

    要删除其中一个将适合您。例如,删除 activity_main.xml 中的FrameLayout:

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
    
    </android.support.design.widget.AppBarLayout>
    
    <include layout="@layout/content_main" />
    
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />
    

    并删除 MainAcitivty.java中的以下部分:

    if (savedInstanceState == null) {
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.container, new ForecastFragment())
                        .commit();
            }
    
    1. 为什么你有2个刷新按钮。因为您正在使用acitivity_main.xml中的工具栏项和使用DarkActionBar的AppTheme。要使用工具栏,您必须使用没有操作栏的主题。

      在AndroidManifest.xml中替换此行。

      "android:theme="@style/AppTheme">"
      

      android:theme="@style/AppTheme.NoActionBar">
      
    2. 然后转到MainActivity并按如下方式启动工具栏

       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
          setSupportActionBar(toolbar);
          if (getSupportActionBar() != null) {
              getSupportActionBar().setDisplayShowTitleEnabled(true);
          }