經過前面的開發,天氣軟件的主體功能已經有了,不過目前存在一個比較嚴重的問題,當你選中一個城市之后,就沒法再去查看其他城市的天氣了,即使退出程序,下次進來的時候還是會直接跳轉到WeatherActivity。這一章節會加入切換城市和手動更新天氣的功能。
(一)手動更新天氣
由於我們在上一節對天氣信息進行了緩存,目前每次展示的都是緩存中的數據,因此需要添加一個能讓用戶手動更新天氣信息。
至於如果出發更新事件呢,這里准備采用下拉刷新的方式。
首先,修改activity_weather.xml中的代碼,如下所示:
1 <FrameLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_height="match_parent" 4 android:layout_width="match_parent" 5 android:background="@color/colorPrimary"> 6 <android.support.v4.widget.SwipeRefreshLayout 7 android:id="@+id/swipe_refresh" 8 android:layout_height="match_parent" 9 android:layout_width="match_parent" 10 <ScrollView 11 android:id="@+id/weather_layout" 12 android:layout_height="match_parent" 13 android:layout_width="match_parent" 14 android:overScrollMode="never" 15 android:scrollbars="none"> 16 17 ... 18 19 </ScrollView> 20 </android.support.v4.widget.SwipeRefreshLayout> 21 </FrameLayout>
可以看到,這里在ScrollView的外面又嵌套了一層SwipeRefreshLayout,這樣ScrollView就自動擁有下拉刷新功能了。
然后修改WeatherActivity中的代碼,加入更新天氣的處理邏輯,如下所示:
1 public class WeatherActivity extends AppCompatActivity { 2 3 public SwipeRefreshLayout swipeRefresh; 4 5 ..... 6 7 @Override 8 protected void onCreate(Bundle savedInstanceState) { 9 super.onCreate(savedInstanceState); 10 .... 11 swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); 12 swipeRefresh.setColorSchemeResources(R.color.colorPrimary); 13 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 14 String weatherString = prefs.getString("weather", null); 15 final String weatherId; 16 if (weatherString != null) { 17 // 有緩存時直接解析天氣數據 18 Weather weather = Utility.handleWeatherResponse(weatherString); 19 weatherId = weather.basic.weatherId; 20 showWeatherInfo(weather); 21 } else { 22 // 無緩存時去服務器查詢天氣 23 weatherId = getIntent().getStringExtra("weather_id"); 24 weatherLayout.setVisibility(View.INVISIBLE); 25 requestWeather(weatherId); 26 } 27 swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 28 @Override 29 public void onRefresh() { 30 requestWeather(weatherId); 31 } 32 }); 33 .... 34 } 35 36 /** 37 * 根據天氣id請求城市天氣信息。 38 */ 39 public void requestWeather(final String weatherId) { 40 String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId + "&key=bc0418b57b2d4918819d3974ac1285d9"; 41 HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() { 42 @Override 43 public void onResponse(Call call, Response response) throws IOException { 44 .... 45 runOnUiThread(new Runnable() { 46 @Override 47 public void run() { 48 if (weather != null && "ok".equals(weather.status)) { 49 SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit(); 50 editor.putString("weather", responseText); 51 editor.apply(); 52 showWeatherInfo(weather); 53 } else { 54 Toast.makeText(WeatherActivity.this, "獲取天氣信息失敗", Toast.LENGTH_SHORT).show(); 55 } 56 swipeRefresh.setRefreshing(false); 57 } 58 }); 59 } 60 61 @Override 62 public void onFailure(Call call, IOException e) { 63 e.printStackTrace(); 64 runOnUiThread(new Runnable() { 65 @Override 66 public void run() { 67 Toast.makeText(WeatherActivity.this, "獲取天氣信息失敗", Toast.LENGTH_SHORT).show(); 68 swipeRefresh.setRefreshing(false); 69 } 70 }); 71 } 72 }); 73 loadBingPic(); 74 } 75 ... 76 }
修改的代碼並不算多,首先在onCreate()方法中獲取到了SwipeRefreshLayout的實例,然后調用setColorSchemeResources()方法來設置下拉刷新進度條的顏色。接着定義了一個weatherId變量,用於記錄城市的天氣id,然后調用setOnRefreshListener()方法來設置一個下拉刷新的監聽器,當觸發了下拉刷新操作的時候,就會回調這個監聽器的onRefresh()方法,我們在這里去調用requestWeather()方法請求天氣信息就可以了。當請求結束后,還需要調用SwipeRefreshLayout的setRefreshing()方法並傳入false,用於表示刷新事件結束,並隱藏刷新進度條。更新完天氣信息后,下拉進度條會自動消失。
(二)切換城市
完成了手動更新天氣的功能,接下來我們繼續實現切換城市功能。
既然要切換城市,那么就需要遍歷省市縣的數據,而這個功能我們在前面就已經完成,並且當時考慮為了后面的復用,特意選擇了在碎片中實現。因此,我們其實只需要在天氣界面的布局中引入這個碎片,就可以快速集成切換城市的功能。
為了不讓引入的碎片把天氣界面遮擋住,我們可以使用滑動菜單功能,將碎片放入到滑動菜單中,正常情況下它不占據主界面的任何空間,想要切換城市的時候,只需要通過滑動的方式將菜單顯示出來就可以了。
首先按照Material Design的建議,我們需要在頭布局中加入一個切換城市的按鈕,這樣可以提示用戶屏幕的左側邊緣是可以拖動的。修改title.xml中的代碼,如下所示:
1 <Relativelayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="matchi_parent" 4 android:layout_height="?attr/actionBarSize"> 5 <Button 6 android:id="@+id/nav_button" 7 android:layout_width="30dp" 8 android:layout_height="30dp" 9 android:layout_marginleft="10dp" 10 android:layout_alignParentLeft="true" 11 android:layout_centerVertical="true" 12 android:background="@drawable/ic_home"/> 13 14 .... 15 16 </RelativeLayout>
這里添加了一個Button作為切換城市的按鈕,並且讓它居左顯示。另外,准備了一張圖片來作為按鈕的背景圖。
接着修改activity_weather.xml布局來加入滑動菜單功能,如下所示:
1 <FrameLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_height="match_parent" 4 android:layout_width="match_parent" 5 android:background="@color/colorPrimary"> 6 .... 7 <android.support.v4.widget.DrawerLayout 8 android:id="@+id/drawer_layout" 9 android:layout_height="match_parent" 10 android:layout_width="match_parent" 11 <android.support.v4.widget.SwipeRefreshLayout 12 android:id="@+id/swipe_refresh" 13 android:layout_height="match_parent" 14 android:layout_width="match_parent" 15 .... 16 </android.support.v4.widget.SwipeRefreshLayout> 17 <fragment 18 android:id="@+id/choose_area_fragment" 19 android:name="com.coolweather.android.ChooseAreaFragment" 20 android:layout_height="match_parent" 21 android:layout_width="match_parent" 22 android:layout_gravity="start"/> 23 </android.support.v4.widget.DrawerLayout 24 </FrameLayout>
可以看到,我們在SwipeRefreshLayout的外面又嵌套了一層DrawerLayout。DrawerLayout中的第一個子控件用於作為主屏幕中顯示的內容,第二個子控件用於作為滑動菜單中顯示的內容,因此這里我們在第二個子控件的位置添加了用於遍歷省市縣數據的碎片。
接下來在WeatherActivity中加入滑動菜單的邏輯處理,修改WeatherActivity中的代碼,如下所示:
1 public class WeatherActivity extends AppCompatActivity{ 2 public DrawerLayout drawerLayout; 3 private Button navButton; 4 ... 5 @Override 6 protected void onCreate(Bundle savedInstanceState){ 7 super.onCreate(savedInstanceState); 8 .... 9 drawerLayout=(Drawerlayout)findViewById(R.id.drawer_layout); 10 navButton=(Button)findViewById(R.id.nav_button); 11 ... 12 navButton.setOnClickListener(new View.OnClickListener(){ 13 @Override 14 public void onClick(View v){ 15 drawerlayout.openDrawer(GravityCompat.START); 16 } 17 }); 18 } 19 .... 20 }
首先在onCreate()方法中獲取到新增的DrawerLayout和Button實例,然后在Button的點擊事件中調用DrawerLayout的openDrawer()方法來打開滑動菜單就可以了。
之后我們還需要處理切換城后的邏輯才行。這個工作就必須要在ChooseAreaFragment中進行了,因為之前選中了某個城市后是跳到WeatherActivity的,而現在由於我們本來就是在WeatherActivity當中,因此並不需要跳轉,只是去請求新選擇城市的天氣信息就可以了。
那么這里,我們需要根據ChooseAreaFragment的不同狀態來進行不同的邏輯處理,修改ChooseAreaFragment中的代碼,如下所示:
1 public class ChooseAreaFragment extends Fragment { 2 3 .... 4 5 @Override 6 public void onActivityCreated(Bundle savedInstanceState) { 7 super.onActivityCreated(savedInstanceState); 8 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 9 @Override 10 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 11 if (currentLevel == LEVEL_PROVINCE) { 12 selectedProvince = provinceList.get(position); 13 queryCities(); 14 } else if (currentLevel == LEVEL_CITY) { 15 selectedCity = cityList.get(position); 16 queryCounties(); 17 } else if (currentLevel == LEVEL_COUNTY) { 18 String weatherId = countyList.get(position).getWeatherId(); 19 if (getActivity() instanceof MainActivity) { 20 Intent intent = new Intent(getActivity(), WeatherActivity.class); 21 intent.putExtra("weather_id", weatherId); 22 startActivity(intent); 23 getActivity().finish(); 24 } else if (getActivity() instanceof WeatherActivity) { 25 WeatherActivity activity = (WeatherActivity) getActivity(); 26 activity.drawerLayout.closeDrawers(); 27 activity.swipeRefresh.setRefreshing(true); 28 activity.requestWeather(weatherId); 29 } 30 } 31 } 32 }); 33 ... 34 } 35 .... 36 }
這里使用一個java中的小技巧,instanceof關鍵字可以用來判斷一個對象是否屬於某個類的實例。我們在碎片中調用getActivity()方法,然后配合instanceof關鍵字,就能輕松判斷出該碎片是在MainActivity當中,還是在WeatherAcitivity當中。如果是在MainActivity當中,那么處理邏輯不變。如果是在WeatherActivity當中,那么就關閉滑動菜單,顯示下拉刷新進度條,然后請求新城市的天氣信息。
這樣我們就把切換城市的功能全部完成了。
下一章節開發后台自動更新的功能。
具體實現步驟連接:
android開發學習之路——天氣預報之技術分析與數據庫(一)
android開發學習之路——天氣預報之遍歷省市縣數據(二)