经过前面的开发,天气软件的主体功能已经有了,不过目前存在一个比较严重的问题,当你选中一个城市之后,就没法再去查看其他城市的天气了,即使退出程序,下次进来的时候还是会直接跳转到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开发学习之路——天气预报之遍历省市县数据(二)