安卓学习专栏——实战项目酷欧天气(5)手动更新天气和切换城市

本文主要是介绍安卓学习专栏——实战项目酷欧天气(5)手动更新天气和切换城市,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

步骤

  • 系列文章
  • 前言
    • 实现效果
    • 项目结构
    • 1.实现下拉刷新
      • 1.1修改修改activity_weather.xml
      • 1.2修改WeatherActivity
      • 1.3目前的效果
    • 2.切换城市
      • 2.1修改WeatherActivity
      • 2.2修改title.xml
      • 2.2修改activity_weather.xml
      • 2.4修改WeatherActivity
      • 2.5修改ChooseAreaFragment
      • 2.6实现效果
    • 附录.参考资料
    • 下载资源
  • 总结

系列文章

提示:转到安卓学习专栏,观看更多内容!
点我直达–>安卓学习专栏
本项目注意包名前缀的修改,改成你自己的,我的包名是:
com.example.coolweather,不然会报错。


前言

本次主题:手动更新天气和切换城市
项目实战继承前面的文章:
安卓学习专栏——实战项目酷欧天气(4)给天气页面加上背景图片
传送门:

https://blog.csdn.net/u011027547/article/details/121524163

实现效果

手动更新天气
在这里插入图片描述
切换城市
在这里插入图片描述


项目结构

在com.coolweather.android包下几个包

  1. db包用于存放数据库模型相关的代码
  2. gson包用于存放GSON模型相关的代码
  3. service包用于存放服务相关的代码
  4. util包用于存放工具相 关的代码。

在这里插入图片描述


1.实现下拉刷新

目前选中了一个城市之后,没法查看其他城市的天气了,即使退出程序,因为我们设置了缓存的原因,下次进来的时候还会直接跳转到WeatherActivity这样明显是很不合理的,所以要能够手动更新天气和切换城市。

1.1修改修改activity_weather.xml

在ScrollView的外面又嵌套了一层SwipeRefreshLayout,这样ScrollView就自动拥有下拉刷新功能了。

在这个过程中,可能会飘红报错
在这里插入图片描述
参考博文添加依赖安卓报错android.support.v4.widget.SwipeRefreshLayout飘红
原来的“<ScrollView”外面添加代码

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayoutandroid:id="@+id/swipe_refresh"android:layout_width="match_parent"android:layout_height="match_parent"><ScrollView//省略···</ScrollView></androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

完整代码

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/colorPrimary"><ImageViewandroid:id="@+id/bing_pic_img"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop" /><androidx.swiperefreshlayout.widget.SwipeRefreshLayoutandroid:id="@+id/swipe_refresh"android:layout_width="match_parent"android:layout_height="match_parent"><ScrollViewandroid:id="@+id/weather_layout"android:layout_width="match_parent"android:layout_height="match_parent"android:overScrollMode="never"android:scrollbars="none"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:fitsSystemWindows="true"android:orientation="vertical"><include layout="@layout/title" /><include layout="@layout/now" /><include layout="@layout/forecast" /><include layout="@layout/aqi" /><include layout="@layout/suggestion" /></LinearLayout></ScrollView></androidx.swiperefreshlayout.widget.SwipeRefreshLayout></FrameLayout>

1.2修改WeatherActivity

修改WeatherActivity中的代码,加入更新天气的处理逻辑。
先在onCreate() 方法中获取到了SwipeRefreshLayout的实例,然后setColorSchemeResources()方法来设置下拉刷新进度条的颜色,这里我们就使用主题中的colorPrimary作为进度条的颜色了。

接着定义了一个mWeatherId 变量,用于记录城市的天气id,然后调用setOnRefreshListener() 方法来设置一个下拉刷新的监听器,当触发了下拉刷新操作的时候,就会回调这个监听器的onRefresh() 方法,我们在这里去调用requestWeather()方法请求天气信息就可以了。

当请求结束后,还需要调用SwipeRefreshLayout的setRefreshing() 方法并传入false ,用于表示刷新事件结束,并隐藏刷新进度条。

完整代码

package com.example.coolweather;import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;import com.bumptech.glide.Glide;
import com.example.coolweather.gson.Forecast;
import com.example.coolweather.gson.Weather;
import com.example.coolweather.util.HttpUtil;
import com.example.coolweather.util.Utility;import java.io.IOException;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;public class WeatherActivity extends AppCompatActivity {private ScrollView weatherLayout;private TextView titleCity;private TextView titleUpdateTime;private TextView degreeText;private TextView weatherInfoText;private LinearLayout forecastLayout;private TextView aqiText;private TextView pm25Text;private TextView comfortText;private TextView carWashText;private TextView sportText;private ImageView bingPicImg;public SwipeRefreshLayout swipeRefresh;private String mWeatherId;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_weather);if (Build.VERSION.SDK_INT >= 21) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);getWindow().setStatusBarColor(Color.TRANSPARENT);}// 初始化各控件weatherLayout = (ScrollView) findViewById(R.id.weather_layout);titleCity = (TextView) findViewById(R.id.title_city);titleUpdateTime = (TextView) findViewById(R.id.title_update_time);degreeText = (TextView) findViewById(R.id.degree_text);weatherInfoText = (TextView) findViewById(R.id.weather_info_text);forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);aqiText = (TextView) findViewById(R.id.aqi_text);pm25Text = (TextView) findViewById(R.id.pm25_text);comfortText = (TextView) findViewById(R.id.comfort_text);carWashText = (TextView) findViewById(R.id.car_wash_text);sportText = (TextView) findViewById(R.id.sport_text);SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);String weatherString = prefs.getString("weather", null);swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);swipeRefresh.setColorSchemeResources(R.color.colorPrimary);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() {@Overridepublic void onRefresh() {requestWeather(mWeatherId);}});bingPicImg = (ImageView) findViewById(R.id.bing_pic_img);String bingPic = prefs.getString("bing_pic", null);if (bingPic != null) {Glide.with(this).load(bingPic).into(bingPicImg);} else {loadBingPic();}}/*** 根据天气id请求城市天气信息*/public void requestWeather(final String weatherId) {String weatherUrl = "http://guolin.tech/api/weather?cityid=" +weatherId + "&key=bc0418b57b2d4918819d3974ac1285d9";HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {final String responseText = response.body().string();final Weather weather = Utility.handleWeatherResponse(responseText);runOnUiThread(new Runnable() {@Overridepublic 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, "获取天气信息失败",Toast.LENGTH_SHORT).show();}swipeRefresh.setRefreshing(false);}});}@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(WeatherActivity.this, "获取天气信息失败",Toast.LENGTH_SHORT).show();swipeRefresh.setRefreshing(false);}});}});}/*** 加载必应每日一图*/private void loadBingPic() {String requestBingPic = "http://guolin.tech/api/bing_pic";HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {final String bingPic = response.body().string();SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();editor.putString("bing_pic", bingPic);editor.apply();runOnUiThread(new Runnable() {@Overridepublic void run() {Glide.with(WeatherActivity.this).load(bingPic).into(bingPicImg);}});}@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}});}/*** 处理并展示Weather实体类中的数据*/private void showWeatherInfo(Weather weather) {String cityName = weather.basic.cityName;String updateTime = weather.basic.update.updateTime.split(" ")[1];String degree = weather.now.temperature + "℃";String weatherInfo = weather.now.more.info;titleCity.setText(cityName);titleUpdateTime.setText(updateTime);degreeText.setText(degree);weatherInfoText.setText(weatherInfo);forecastLayout.removeAllViews();for (Forecast forecast : weather.forecastList) {View view = LayoutInflater.from(this).inflate(R.layout.forecast_item, forecastLayout, false);TextView dateText = (TextView) view.findViewById(R.id.date_text);TextView infoText = (TextView) view.findViewById(R.id.info_text);TextView maxText = (TextView) view.findViewById(R.id.max_text);TextView minText = (TextView) view.findViewById(R.id.min_text);dateText.setText(forecast.date);infoText.setText(forecast.more.info);maxText.setText(forecast.temperature.max);minText.setText(forecast.temperature.min);forecastLayout.addView(view);}if (weather.aqi != null) {aqiText.setText(weather.aqi.city.aqi);pm25Text.setText(weather.aqi.city.pm25);}String comfort = "舒适度:" + weather.suggestion.comfort.info;String carWash = "洗车指数:" + weather.suggestion.carWash.info;String sport = "运动建议:" + weather.suggestion.sport.info;comfortText.setText(comfort);carWashText.setText(carWash);sportText.setText(sport);weatherLayout.setVisibility(View.VISIBLE);}
}

1.3目前的效果

在这里插入图片描述


2.切换城市

当时为了方便后面的复用,特意选择了在碎片当中实现。现在只需要在天气界面的布局中引入这个碎片,就可以快速集成切换城市功能了

2.1修改WeatherActivity

修改WeatherActivity,滑动菜单功能。
将碎片放入到滑动菜单中,正常情况下它不占据主界面的任何空间,想要切换城市的时候只需要通过滑动的方式将菜单显示出来就可以了。

2.2修改title.xml

按照Material Design的建议,我们需要在头布局中加入一个切换城市的按钮,不然的话用户可能根本就不知道屏幕的左侧边缘是可以拖动的。

在res/drawable文件夹下添加图片ic_home.png
在这里插入图片描述
在这里插入图片描述
添加了一个Button作为切换城市的按钮,并且让它居左显示。图片来作为按钮的背景图

添加代码

    <Buttonandroid: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" />

完整代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"><Buttonandroid: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" /><TextViewandroid:id="@+id/title_city"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:textColor="#fff"android:textSize="20sp" /><TextViewandroid:id="@+id/title_update_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:textColor="#fff"android:textSize="16sp" />
</RelativeLayout>

2.2修改activity_weather.xml

修改activity_weather.xml布局来加入滑动菜单功能

在SwipeRefreshLayout的外面又嵌套了一层DrawerLayout。DrawerLayout中的第一个子控件用于作为主屏幕中显示的内容,第二个子控件用于作为滑动菜单中显示的内容,因此这里我们在第二个子控件的位置添加了用于遍历省市县数据的碎片。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/colorPrimary"><ImageViewandroid:id="@+id/bing_pic_img"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop" /><androidx.drawerlayout.widget.DrawerLayoutandroid:id="@+id/drawer_layout"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.swiperefreshlayout.widget.SwipeRefreshLayoutandroid:id="@+id/swipe_refresh"android:layout_width="match_parent"android:layout_height="match_parent"><ScrollViewandroid:id="@+id/weather_layout"android:layout_width="match_parent"android:layout_height="match_parent"android:overScrollMode="never"android:scrollbars="none"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:fitsSystemWindows="true"android:orientation="vertical"><include layout="@layout/title" /><include layout="@layout/now" /><include layout="@layout/forecast" /><include layout="@layout/aqi" /><include layout="@layout/suggestion" /></LinearLayout></ScrollView></androidx.swiperefreshlayout.widget.SwipeRefreshLayout><fragmentandroid:id="@+id/choose_area_fragment"android:name="com.example.coolweather.ChooseAreaFragment"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="start" /></androidx.drawerlayout.widget.DrawerLayout></FrameLayout>

2.4修改WeatherActivity

在WeatherActivity中加入滑动菜单的逻辑处理

在onCreate() 方法中获取到新增的DrawerLayout和Button的实例,然后在Button的点击事件中调用DrawerLayout的openDrawer() 方法来打开滑动菜单

package com.example.coolweather;import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;import com.bumptech.glide.Glide;
import com.example.coolweather.gson.Forecast;
import com.example.coolweather.gson.Weather;
import com.example.coolweather.util.HttpUtil;
import com.example.coolweather.util.Utility;import java.io.IOException;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;public class WeatherActivity extends AppCompatActivity {private ScrollView weatherLayout;private TextView titleCity;private TextView titleUpdateTime;private TextView degreeText;private TextView weatherInfoText;private LinearLayout forecastLayout;private TextView aqiText;private TextView pm25Text;private TextView comfortText;private TextView carWashText;private TextView sportText;private ImageView bingPicImg;public SwipeRefreshLayout swipeRefresh;private String mWeatherId;// 要在WeatherActivity中加入滑动菜单的逻辑处理public DrawerLayout drawerLayout;private Button navButton;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (Build.VERSION.SDK_INT >= 21) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);getWindow().setStatusBarColor(Color.TRANSPARENT);}setContentView(R.layout.activity_weather);// 初始化各控件weatherLayout = (ScrollView) findViewById(R.id.weather_layout);titleCity = (TextView) findViewById(R.id.title_city);titleUpdateTime = (TextView) findViewById(R.id.title_update_time);degreeText = (TextView) findViewById(R.id.degree_text);weatherInfoText = (TextView) findViewById(R.id.weather_info_text);forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);aqiText = (TextView) findViewById(R.id.aqi_text);pm25Text = (TextView) findViewById(R.id.pm25_text);comfortText = (TextView) findViewById(R.id.comfort_text);carWashText = (TextView) findViewById(R.id.car_wash_text);sportText = (TextView) findViewById(R.id.sport_text);SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);String weatherString = prefs.getString("weather", null);drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);navButton = (Button) findViewById(R.id.nav_button);swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);swipeRefresh.setColorSchemeResources(R.color.colorPrimary);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() {@Overridepublic void onRefresh() {requestWeather(mWeatherId);}});bingPicImg = (ImageView) findViewById(R.id.bing_pic_img);String bingPic = prefs.getString("bing_pic", null);if (bingPic != null) {Glide.with(this).load(bingPic).into(bingPicImg);} else {loadBingPic();}navButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {drawerLayout.openDrawer(GravityCompat.START);}});}/*** 根据天气id请求城市天气信息*/public void requestWeather(final String weatherId) {String weatherUrl = "http://guolin.tech/api/weather?cityid=" +weatherId + "&key=bc0418b57b2d4918819d3974ac1285d9";HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {final String responseText = response.body().string();final Weather weather = Utility.handleWeatherResponse(responseText);runOnUiThread(new Runnable() {@Overridepublic 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, "获取天气信息失败",Toast.LENGTH_SHORT).show();}swipeRefresh.setRefreshing(false);}});}@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(WeatherActivity.this, "获取天气信息失败",Toast.LENGTH_SHORT).show();swipeRefresh.setRefreshing(false);}});}});}/*** 加载必应每日一图*/private void loadBingPic() {String requestBingPic = "http://guolin.tech/api/bing_pic";HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {final String bingPic = response.body().string();SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();editor.putString("bing_pic", bingPic);editor.apply();runOnUiThread(new Runnable() {@Overridepublic void run() {Glide.with(WeatherActivity.this).load(bingPic).into(bingPicImg);}});}@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}});}/*** 处理并展示Weather实体类中的数据*/private void showWeatherInfo(Weather weather) {String cityName = weather.basic.cityName;String updateTime = weather.basic.update.updateTime.split(" ")[1];String degree = weather.now.temperature + "℃";String weatherInfo = weather.now.more.info;titleCity.setText(cityName);titleUpdateTime.setText(updateTime);degreeText.setText(degree);weatherInfoText.setText(weatherInfo);forecastLayout.removeAllViews();for (Forecast forecast : weather.forecastList) {View view = LayoutInflater.from(this).inflate(R.layout.forecast_item, forecastLayout, false);TextView dateText = (TextView) view.findViewById(R.id.date_text);TextView infoText = (TextView) view.findViewById(R.id.info_text);TextView maxText = (TextView) view.findViewById(R.id.max_text);TextView minText = (TextView) view.findViewById(R.id.min_text);dateText.setText(forecast.date);infoText.setText(forecast.more.info);maxText.setText(forecast.temperature.max);minText.setText(forecast.temperature.min);forecastLayout.addView(view);}if (weather.aqi != null) {aqiText.setText(weather.aqi.city.aqi);pm25Text.setText(weather.aqi.city.pm25);}String comfort = "舒适度:" + weather.suggestion.comfort.info;String carWash = "洗车指数:" + weather.suggestion.carWash.info;String sport = "运动建议:" + weather.suggestion.sport.info;comfortText.setText(comfort);carWashText.setText(carWash);sportText.setText(sport);weatherLayout.setVisibility(View.VISIBLE);}
}

2.5修改ChooseAreaFragment

根据ChooseAreaFragment的不同状态来进行不同的逻辑处理。

instanceof 关键字可以用来判断一个对象是否属于某个类的实例。我们在碎片中调用getActivity()
方法,然后配合instanceof 关键字,就能轻松判断出该碎片是在MainActivity当中,还是WeatherActivity当中。如果是在MainActivity当中,那么处理逻辑不变。如果是在WeatherActivity当中,那么就关闭滑动菜单,显示下拉刷新进度条,然后请求新城市的天气信息。

package com.example.coolweather;import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;import com.example.coolweather.R;
import com.example.coolweather.db.City;
import com.example.coolweather.db.County;
import com.example.coolweather.db.Province;
import com.example.coolweather.util.HttpUtil;
import com.example.coolweather.util.Utility;import org.litepal.crud.DataSupport;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import androidx.fragment.app.Fragment;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;public class ChooseAreaFragment extends Fragment {public static final int LEVEL_PROVINCE = 0;public static final int LEVEL_CITY = 1;public static final int LEVEL_COUNTY = 2;private ProgressDialog progressDialog;private TextView titleText;private Button backButton;private ListView listView;private ArrayAdapter<String> adapter;private List<String> dataList = new ArrayList<>();/*** 省列表*/private List<Province> provinceList;/*** 市列表*/private List<City> cityList;/*** 县列表*/private List<County> countyList;/*** 选中的省份*/private Province selectedProvince;/*** 选中的城市*/private City selectedCity;/*** 当前选中的级别*/private int currentLevel;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.choose_area, container, false);titleText = (TextView) view.findViewById(R.id.title_text);backButton = (Button) view.findViewById(R.id.back_button);listView = (ListView) view.findViewById(R.id.list_view);adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, dataList);listView.setAdapter(adapter);return view;}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic 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);}}}});backButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (currentLevel == LEVEL_COUNTY) {queryCities();} else if (currentLevel == LEVEL_CITY) {queryProvinces();}}});queryProvinces();}/*** 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询*/private void queryProvinces() {titleText.setText("中国");backButton.setVisibility(View.GONE);provinceList = DataSupport.findAll(Province.class);if (provinceList.size() > 0) {dataList.clear();for (Province province : provinceList) {dataList.add(province.getProvinceName());}adapter.notifyDataSetChanged();listView.setSelection(0);currentLevel = LEVEL_PROVINCE;} else {String address = "http://guolin.tech/api/china";queryFromServer(address, "province");}}/*** 查询选中省内所有的市,优先从数据库查询,如果没有查询到再去服务器上查询*/private void queryCities() {titleText.setText(selectedProvince.getProvinceName());backButton.setVisibility(View.VISIBLE);cityList = DataSupport.where("provinceid = ?", String.valueOf(selectedProvince.getId())).find(City.class);if (cityList.size() > 0) {dataList.clear();for (City city : cityList) {dataList.add(city.getCityName());}adapter.notifyDataSetChanged();listView.setSelection(0);currentLevel = LEVEL_CITY;} else {int provinceCode = selectedProvince.getProvinceCode();String address = "http://guolin.tech/api/china/" + provinceCode;queryFromServer(address, "city");}}/*** 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器上查询*/private void queryCounties() {titleText.setText(selectedCity.getCityName());backButton.setVisibility(View.VISIBLE);countyList = DataSupport.where("cityid = ?", String.valueOf(selectedCity.getId())).find(County.class);if (countyList.size() > 0) {dataList.clear();for (County county : countyList) {dataList.add(county.getCountyName());}adapter.notifyDataSetChanged();listView.setSelection(0);currentLevel = LEVEL_COUNTY;} else {int provinceCode = selectedProvince.getProvinceCode();int cityCode = selectedCity.getCityCode();String address = "http://guolin.tech/api/china/" + provinceCode + "/" +cityCode;queryFromServer(address, "county");}}/*** 根据传入的地址和类型从服务器上查询省市县数据*/private void queryFromServer(String address, final String type) {showProgressDialog();HttpUtil.sendOkHttpRequest(address, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {String responseText = response.body().string();boolean result = false;if ("province".equals(type)) {result = Utility.handleProvinceResponse(responseText);} else if ("city".equals(type)) {result = Utility.handleCityResponse(responseText,selectedProvince.getId());} else if ("county".equals(type)) {result = Utility.handleCountyResponse(responseText,selectedCity.getId());}if (result) {getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {closeProgressDialog();if ("province".equals(type)) {queryProvinces();} else if ("city".equals(type)) {queryCities();} else if ("county".equals(type)) {queryCounties();}}});}}@Overridepublic void onFailure(Call call, IOException e) {
// 通过runOnUiThread()方法回到主线程处理逻辑getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {closeProgressDialog();Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT).show();}});}});}/*** 显示进度对话框*/private void showProgressDialog() {if (progressDialog == null) {progressDialog = new ProgressDialog(getActivity());progressDialog.setMessage("正在加载...");progressDialog.setCanceledOnTouchOutside(false);}progressDialog.show();}/*** 关闭进度对话框*/private void closeProgressDialog() {if (progressDialog != null) {progressDialog.dismiss();}}
}

2.6实现效果

在这里插入图片描述


附录.参考资料

《第一行代码》14.6 手动更新天气和切换城市


下载资源

gitee地址

https://gitee.com/miao-zehao/cool-weather

在这里插入图片描述

安卓学习者实战项目酷欧天气(5)手动更新天气和切换城市示例


总结

大家喜欢的话,给个👍,点个关注!继续跟大家分享敲代码过程中遇到的问题!


这篇关于安卓学习专栏——实战项目酷欧天气(5)手动更新天气和切换城市的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/394421

相关文章

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

在Spring Boot中集成RabbitMQ的实战记录

《在SpringBoot中集成RabbitMQ的实战记录》本文介绍SpringBoot集成RabbitMQ的步骤,涵盖配置连接、消息发送与接收,并对比两种定义Exchange与队列的方式:手动声明(... 目录前言准备工作1. 安装 RabbitMQ2. 消息发送者(Producer)配置1. 创建 Spr

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

MySQL中的索引结构和分类实战案例详解

《MySQL中的索引结构和分类实战案例详解》本文详解MySQL索引结构与分类,涵盖B树、B+树、哈希及全文索引,分析其原理与优劣势,并结合实战案例探讨创建、管理及优化技巧,助力提升查询性能,感兴趣的朋... 目录一、索引概述1.1 索引的定义与作用1.2 索引的基本原理二、索引结构详解2.1 B树索引2.2