下面手动更新天气和切换城市
1. 手动更新天气
由于在之前对天气进行了缓存,目前每次展示的都是之前的缓存数据,因此需要实现用户手动更新天气。
首先修改activity_weather.xml中代码,在ScrollView的外面又嵌套了一层SwipeRefreshLayout,这样ScrollView就自动拥有了刷新功能了。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
...
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/weather_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none">
...
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
然后修改WeatherActivity中代码,首先在onCreate()方法中获取SwipeRefreshLayout的实例,然后调用setColorSchemeResources()方法来设置下拉刷新进度条的颜色,接着定义一个mWeatherId变量用来记录天气id,然后调用setOnRefreshListener()方法来设置一个下拉刷新的监听器,最后请求结束吼,调用setRefreshing()方法并传入false,用于表示刷新时间结束,隐藏刷新进度条。
public class WeatherActivity extends AppCompatActivity {
//我是新增的
public SwipeRefreshLayout swipeRefresh;
private String mWeatherId;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//我是新增的
swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String weatherString = prefs.getString("weather", null);
//我是新增的
if (weatherString != null) {
//有缓存直接解析天气信息
Weather weather = Utility.handleWeatherResponse(weatherString);
mWeatherId = weather.basic.weatherId;
showWeatherInfo(weather);
} else {
// 无缓存去服务器查询天气
mWeatherId = getIntent().getStringExtra("weather_id");
weatherLayout.setVisibility(View.INVISIBLE);
requestWeather(mWeatherId);
}
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
requestWeather(mWeatherId);
}
});
...
}
public void requestWeather(final String weatherId) {
String weatherUrl = "http://guolin.tech/api/weather?cityid=" +
weatherId + "&key=9618c9b7080b4638a16fca8687bf9a60";
HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(WeatherActivity.this, "获取天气信息失败1",
Toast.LENGTH_SHORT).show();
//我是新增的
swipeRefresh.setRefreshing(false);
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
final Weather weather = Utility.handleWeatherResponse(responseText);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (weather != null && "ok".equals(weather.status)) {
SharedPreferences.Editor editor = PreferenceManager.
getDefaultSharedPreferences(WeatherActivity.this).edit();
editor.putString("weather", responseText);
editor.apply();
//我是新增的
mWeatherId=weather.basic.weatherId;
showWeatherInfo(weather);
} else {
Toast.makeText(WeatherActivity.this, "获取天气信息失败2",
Toast.LENGTH_SHORT).show();
}
//我是新增的
swipeRefresh.setRefreshing(false);
}
});
}
});
loadBingPic();
}
}
2. 切换城市
首先按照Material Design 的建议,我们需要在头部布局中加一个切换城市的按钮,不然用户不知道左侧边缘是可以拖动的。修改title.xml中的代码,添加一个BUtton作为切换城市的按钮。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<Button
android:id="@+id/nav_button"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="10dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/ic_home"></Button>
...
</RelativeLayout>
然后修改activity_weather.xml来加入滑动菜单功能,在SwipeRefreshLayout的外面嵌套了一层DrawerLayout。DrawerLayout中第一个子控件用于作为屏幕中显示的内容,第二个子控件用于作为滑动菜单中显示的内容,因此在第二个子控件的位置添加了用于遍历省市县的数据碎片。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
...
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
</android.support.v4.widget.SwipeRefreshLayout>
<fragment
android:id="@+id/choose_area_fragment"
android:name="com.example.dean.deanweather.ChooseAreaFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"></fragment>
</android.support.v4.widget.DrawerLayout>
</FrameLayout>
然后需要在WeatherActivity中加入滑动菜单的逻辑处理,修改WeatherActivity中的代码,首先在onCreate()方法中获取新增的DrawerLayout和Button 的实例,然后在button点击事件中调用DrawerLayout的openDrawer()方法来打开滑动菜单。
public class WeatherActivity extends AppCompatActivity {
public DrawerLayout drawerLayout;
private Button navButton;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navButton = (Button) findViewById(R.id.nav_button);
...
navButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.openDrawer(GravityCompat.START);
}
});
}
}
上面仅仅是打开了滑动菜单而已,还需要处理切换城市后的逻辑。之前选中了某个城市后是跳转到WeatherActivity的,而现在由于外面本来是在WeatherActivity当中的,因此不需要跳转,只是去请求新选择城市的天气信息就行了。因此需要根据ChooseAreaFragment的不同状态来进行不同的逻辑处理,修改ChooseAreaFragment中的代码,instanceof 关键字可以用来判断一个对象是否属于某个类的实例。我们在碎片中调用个体Activity()方法,然后配合instanceof关键字,就能轻松的判断该碎片是MainActivity还是WeatherActivity当中。如果是在MainActivity,那么逻辑不变。如果是在WeatherActivity当中,那么关掉滑动菜单,显示下拉刷新进度条,然后请求新的城市的天气信息。
public class ChooseAreaFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (currentLevel == LEVEL_PROVINCE) {
selectedProvince = provinceList.get(position);
queryCities();
} else if (currentLevel == LEVEL_CITY) {
selectedCity = cityList.get(position);
queryCounties();
} else if (currentLevel == LEVEL_COUNTY) {
String weatherId = countyList.get(position).getWeatherId();
//我是新增的
if(getActivity() instanceof MainActivity) {
Intent intent = new Intent(getActivity(), WeatherActivity.class);
intent.putExtra("weather_id", weatherId);
startActivity(intent);
getActivity().finish();
}else if(getActivity() instanceof WeatherActivity) {
WeatherActivity activity = (WeatherActivity) getActivity();
activity.drawerLayout.closeDrawers();
activity.swipeRefresh.setRefreshing(true);
activity.requestWeather(weatherId);
}
}
}
});
...
}
}


3. 后台自动更新天气
想要实现上述功能,需要创建一个长期在后台运行的定时任务。
首先在service包下新建一个服务,创建AutoUpdateService,在onStartCommand()方法中先调用了updateWeather() 方法来更新天气,然后调用了updateBingPic()方法来更新背景图片。这里直接存储到SharedPreferences文件中就可以了,因为打开WeatherActivity的时候会优先从SharedPreferences缓存中读取数据。将时间间隔设置为8小时,8小时后onStartCommand()方法重新执行。
public class AutoUpdateService extends Service {
public AutoUpdateService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//throw new UnsupportedOperationException("Not yet implemented");
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
updateWeather();
updateBingPic();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anhour = 8 * 60 * 60 * 1000;//8小时
long triggerAtTime = SystemClock.elapsedRealtime() + anhour;
Intent i = new Intent(this, AutoUpdateService.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
manager.cancel(pi);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId);
}
private void updateWeather() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String weatherString = prefs.getString("weather", null);
if (weatherString != null) {
Weather weather = Utility.handleWeatherResponse(weatherString);
final String weatherId = weather.basic.weatherId;
String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId +
"&key=9618c9b7080b4638a16fca8687bf9a60";
HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseText = response.body().string();
Weather weather = Utility.handleWeatherResponse(responseText);
if (weather != null && "ok".equals(weather.status)) {
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(
AutoUpdateService.this).edit();
editor.putString("weather", responseText);
editor.apply();
}
}
});
}
}
/**
* 更新每日一图
*/
private void updateBingPic() {
String requestBingPic = "http://guolin.tech/api/bing_pic";
HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String bingPic = response.body().string();
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(
AutoUpdateService.this).edit();
editor.putString("bing_pic", bingPic);
editor.apply();
}
});
}
}
最后去激活AutoUpdateService 这个服务就行
private void showWeatherInfo(Weather weather) {
...
Intent intent = new Intent(this,AutoUpdateService.class);
startService(intent);
}
基本功能就已经完成了,后续会继续添加其他功能
网友评论