一、项目框架的搭建
1、在git上新建一个coolweather的仓库
2、新建一个Coolweather项目,添加到git版本管理
3、在java中新建几个包,db:用于存放数据库模型相关的代码,gson:用于存放GSon相关的代码,service:用于存放服务相关的代码,util包用于存放工具相关代码
4、添加项目依赖的第三方,在app/build.gradle
依赖:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'org.litepal.android:core:2.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
仓库添加
allprojects {
repositories {
mavenCentral()
google()
jcenter()
}
}
说明:
implementation 'org.litepal.android:core:2.0.0'
是数据库操作的框架
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
网络请求框架
implementation 'com.google.code.gson:gson:2.8.5'
JSON解析框架
implementation 'com.github.bumptech.glide:glide:4.7.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
为加载图片的框架
二、数据库表的设计
1、数据获取
省份获取接口:
http://guolin.tech/api/china
获取的数据如下:
[{"id":1,"name":"北京"},{"id":2,"name":"上海"}]
省下面对应的市获取接口
http://guolin.tech/api/china/22,其中22
为省份的id,数据结构如下:
[{"id":191,"name":"长沙"},{"id":192,"name":"湘潭"},{"id":193,"name":"株洲"},{"id":194,"name":"衡阳"},{"id":195,"name":"郴州"},{"id":196,"name":"常德"},{"id":197,"name":"益阳"},{"id":198,"name":"娄底"},{"id":199,"name":"邵阳"},{"id":200,"name":"岳阳"},{"id":201,"name":"张家界"},{"id":202,"name":"怀化"},{"id":203,"name":"永州"},{"id":204,"name":"吉首"}]
获取区对应的信息
http://guolin.tech/api/china/22/191,其中191为市的id
[{"id":1422,"name":"长沙","weather_id":"CN101250101"},{"id":1423,"name":"宁乡","weather_id":"CN101250102"},{"id":1424,"name":"浏阳","weather_id":"CN101250103"},{"id":1425,"name":"马坡岭","weather_id":"CN101250104"},{"id":1426,"name":"望城","weather_id":"CN101250105"}]
最后通过weather_id 获取天气信息,使用和风天气的api
https://console.heweather.com/register?role=personal,注册免费
天气信息的获取
https://free-api.heweather.com/s6/weather/now?location=CN101250101&key=22c4dc1c0a3245c7a97e7c2543b9781a
数据如下:
{"HeWeather6":[{"basic":{"cid":"CN101250101","location":"长沙","parent_city":"长沙","admin_area":"湖南","cnty":"中国","lat":"28.19408989","lon":"112.98227692","tz":"+8.00"},"update":{"loc":"2018-07-06 16:49","utc":"2018-07-06 08:49"},"status":"ok","now":{"cloud":"75","cond_code":"104","cond_txt":"阴","fl":"31","hum":"85","pcpn":"0.0","pres":"1000","tmp":"29","vis":"13","wind_deg":"293","wind_dir":"西北风","wind_sc":"3","wind_spd":"19"}}]}
2、数据库表实现
新建Province类 继承LitePalSupport
类
public class Province extends LitePalSupport {
private int id;
private String provinceName;
private int provinceCode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public int getProvinceCode() {
return provinceCode;
}
public void setProvinceCode(int provinceCode) {
this.provinceCode = provinceCode;
}
}
City类
public class City extends LitePalSupport {
private int id;
private String cityName;
private int cityCode;
private int provinceId;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public int getCityCode() {
return cityCode;
}
public void setCityCode(int cityCode) {
this.cityCode = cityCode;
}
public int getProvinceId() {
return provinceId;
}
public void setProvinceId(int provinceId) {
this.provinceId = provinceId;
}
}
Country类
public class Country extends LitePalSupport {
private int id;
private String countryName;
private String weatherId;
private int cityId;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
public String getWeatherId() {
return weatherId;
}
public void setWeatherId(String weatherId) {
this.weatherId = weatherId;
}
public int getCityId() {
return cityId;
}
public void setCityId(int cityId) {
this.cityId = cityId;
}
}
在app/src/main 目录在新建assets 文件,在改目录下新建一个litepal.xml文件,编辑内容如下:
最后需要在配置一下LitePalApplication,修改如下:
3、自定义Fragment
首先定义一个线性布局如下
这里自定义了导航条,所以需要修改style.xml文件,修改如下:
新建一个ChooseAreaFragment继承Fragment,代码如下:
public class ChooseAreaFragment extends Fragment {
public static final int LEVEL_PROVINCE = 0;
public static final int LEVEL_CITY = 1;
public static final int LEVEL_COUNTRY = 2;
private static final String TYPE_PROVINCE = "province";
private static final String TYPE_City = "city";
private static final String TYPE_Country = "country";
private ProgressDialog progressDialog;
private TextView titleText;
private Button backButton;
private ListView listView;
private ArrayAdapter adapter;
private List dataList = new ArrayList<>();
private List provinceList;
private List cityList;
private List countryList;
private Province selectedProvince;
private City selectedCity;
private int currentLevel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable 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.btn_back);
listView = (ListView)view.findViewById(R.id.listView);
adapter = new ArrayAdapter<>(getContext(),android.R.layout.simple_list_item_1,dataList);
listView.setAdapter(adapter);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
if (currentLevel == LEVEL_PROVINCE){
selectedProvince = provinceList.get(i);
queryCities();
}else if (currentLevel == LEVEL_CITY){
selectedCity = cityList.get(i);
queryCounties();
}
}
});
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (currentLevel == LEVEL_COUNTRY){
queryCities();
}else if (currentLevel == LEVEL_CITY){
queryProinces();
}
}
});
queryProinces();
}
private void queryCounties() {
titleText.setText(selectedCity.getCityName());
backButton.setVisibility(View.VISIBLE);
countryList = LitePal.where("cityid = ?",String.valueOf(selectedCity.getId())).find(Country.class);
if (countryList.size()>0){
dataList.clear();
for (Country country:countryList){
dataList.add(country.getCountryName());
}
adapter.notifyDataSetChanged();;
listView.setSelection(0);
currentLevel = LEVEL_COUNTRY;
}else {
int provinceCode = selectedProvince.getProvinceCode();
int cityCode = selectedCity.getCityCode();
String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode;
queryFromServer(address,TYPE_Country);
}
}
private void queryCities() {
titleText.setText(selectedProvince.getProvinceName());
backButton.setVisibility(View.VISIBLE);
cityList = LitePal.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,TYPE_City);
}
}
private void queryProinces() {
titleText.setText("中国");
backButton.setVisibility(View.GONE);
provinceList = LitePal.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,TYPE_PROVINCE);
}
}
private void queryFromServer(String address,final String type){
showProgressDialog();
HttpUtil.sendHttpRequest(address, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
closeProgressDialog();
Toast.makeText(getContext(),"加载失败",Toast.LENGTH_SHORT).show();;
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//在这里解析数据
String responseText = response.body().string();
Log.d(TAG, "onResponse: " + responseText);
boolean result = false;
if (type.equals(TYPE_PROVINCE)){
result = Utility.handleProvinceResponse(responseText);
}else if(type.equals(TYPE_City)){
result = Utility.handleCityResponse(responseText,selectedProvince.getId());
}else if(type.equals(TYPE_Country)){
result = Utility.handleCountryResponse(responseText,selectedCity.getId());
}
if (result){
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
closeProgressDialog();
if (type.equals(TYPE_PROVINCE)){
queryProinces();
}else if(type.equals(TYPE_City)){
queryCities();
}else if(type.equals(TYPE_Country)){
queryCounties();
}
}
});
}
}
});
}
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();
}
}
}
在activity_main.xml文件中添加Fragment布局如下:
三、展示天气数据
效果如下:
将上图的界面拆分成三个布局:1、导航部分(展示城市和更新时间)
2、现在天气信息 3、未来三天信息展示
1、导航部分,新建一个title.xml
,代码如下:
2、天气信息部分
3、未来天气布局forcast.xml
代码如下:
forecast_item.xml
文件
四、生成APK文件
https://blog.csdn.net/wyg1230/article/details/77529465/