我的MOOC课基本结束了,学校要求做的最后一讲是做一个天气查询软件。
完整项目代码地址:
https://github.com/AbitGo/mybatis_csdn/tree/master/workspace_as_mooc25
1.软件思路
- 使用提供的API,并实现HTTP通讯
- 可以通过省、市区(包括直辖市)查询
- 可以通过spinner实现下拉选择
2.实验现象
3.实验步骤
3.1API选择
在本文中使用的是中国天气网的API,以下是网址
https://m.weather.com.cn/d/town/index?lat=31.29834&lon=120.58319&areaid=101190401
3.2获得省/直辖市API
http://www.weather.com.cn/data/city3jdata/china.html
3.3获得市API
http://www.weather.com.cn/data/city3jdata/provshi/10109.html
3.3获得天气API
在这里说明:本问要求使用市作为最小单位,例如常州市、苏州市而非江阴、溧阳等。
非直辖市
需要进行上面的省ID+市区ID+01获得
直辖市则与直辖市不一样,因为直辖市在获取省的时候只有一个返回参数00:
http://www.weather.com.cn/data/sk/101010100.html
使用获取天气APi需要区别直辖与非直辖
这是北京
这是江苏南京
从中看出两者区别在于cityid的位置摆放不一致。
3.4应用网络通讯权限
在AndroidManifest.xml中添加以下代码。
<uses-permission android:name="android.permission.INTERNET" />
3.5外部库
在gradle的dependencies中添加
implementation 'com.squareup.okhttp3:okhttp:3.14.2'
implementation 'com.alibaba:fastjson:1.1.71.android'
3.6HTTP调用
详细代码可以看最后
实验注意点
- 调用外部类需要导入gradle
- 不可以再HTTP通讯代码中写改变UI的代码
- 调用API需要注意直辖市与非直辖市的区别
所有源码
MainActivity.java
package com.example.mooc25hw;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private Spinner spinner_prov,spinner_city;
private TextView textView;
private LinearLayout linearLayout;
private OkHttpUtils okHttpUtils;
//作为省的初始ID
private String provinceId = "10101";
private String cityID = "01";
//作为省名字的数组
private Map<String,String> provinceMap= new HashMap<>();
private ArrayList<String> provinceArray=new ArrayList<>();
//作为市区名字的数组
private Map<String,String> cityMap = new HashMap<>();
private ArrayList<String> cityArray = new ArrayList<>();
//获取省的HTTP请求链接
final String searchPorvURI = "http://www.weather.com.cn/data/city3jdata/china.html";
//获取市区的HTTP请求
//需要使用 searchCityURI+provionceID+searchCityURI_suffix进行请求
final String searchCityURI = "http://www.weather.com.cn/data/city3jdata/provshi/";
final String searchCityURI_suffix = ".html";
final String searchWeatherURI = "http://www.weather.com.cn/data/sk/";
final String searchWeatherURI_suffix = ".html";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
//进行省的HTTP即可
getHTTPLink(searchPorvURI,0);
}
/**
* 名称:获取HTTP连接
* 参数:URI获取JSON的链接、operateId为操作码
* 返回值:无
*/
public void getHTTPLink(String URI,int operateId){
//获得初始化之后的Https连接
okHttpUtils = OkHttpUtils.getInstance();
okHttpUtils.getRequest(URI, new Callback() {
//无操作
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
String json = response.body().string();
switch (operateId){
case 0:
//讲数据传输至省数据更新函数
UpdateProvinceData(json);
break;
case 1:
UpdateCityData(json);
break;
case 2:
UpdateWeatherData(json);
break;
}
}
});
}
/***
* 名称:更新所在市区的天气
* 参数:HTTP传回的JSON数据
* 返回值:无
* 功能说明:通过解析JSON更新市区天气数据
* @param JsonData
* "weatherinfo": {
* "city": "上海",
* "cityid": "101020100",
* "temp": "23.5",
* "WD": "东北风",
* "WS": "小于3级",
* "SD": "80%",
* "AP": "1006.4hPa",
* "njd": "2903",
* "WSE": "<3",
* "time": "17:00",
* "sm": "1.1",
* "isRadar": "1",
* "Radar": "JC_RADAR_AZ9210_JB"
* }
*/
public void UpdateWeatherData(String JsonData){
runOnUiThread(() -> {
try {
//进行JSON数据的反序列化
JSONObject param1 = JSONObject.parseObject(JsonData);
JSONObject param2 =JSONObject.parseObject(param1.get("weatherinfo").toString());
String result = "城市:"+param2.getString("city")+"\n"
+"温度:"+param2.getString("temp")+"\n"
+"湿度:"+param2.getString("SD")+"\n"
+"气压:"+param2.getString("AP")+"\n"
+"天气:"+param2.getString("WD")+"\n";
textView.setText(result);
} catch (JSONException e) {
Toast.makeText(MainActivity.this,"JSON解析失败",Toast.LENGTH_SHORT).show();
}
});
}
/***
* 名称:更新市区信息
* 参数:HTTP传回的JSON数据
* 返回值:无
* 功能说明:通过解析JSON更新市区数据
* @param JsonData {"01": "南京","02": "无锡"}
*/
public void UpdateCityData(String JsonData){
runOnUiThread(() -> {
try {
//进行JSON数据的反序列化
JSONObject param = JSONObject.parseObject(JsonData);
//将CityID编号提取出来
Set<String> cityKey = param.keySet();
Toast.makeText(MainActivity.this,"JSON市区信息获取成功",Toast.LENGTH_SHORT).show();
//数据更新,防止省的市区进行叠加
cityArray.clear();
cityMap.clear();
for(String key:cityKey){
//将数据以key:provinceName存储至provinceList
String cityName = param.getString(key);
cityMap.put(cityName,key);
cityArray.add(cityName);
}
//进行适配器的操作
ArrayAdapter arrayAdapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_dropdown_item_1line, cityArray);
spinner_city.setAdapter(arrayAdapter);
} catch (JSONException e) {
Toast.makeText(MainActivity.this,"JSON解析失败",Toast.LENGTH_SHORT).show();
}
});
}
/**
* 名称:更新省信息
* 参数:HTTP传回的JSON数据
* 返回值:无
* 功能说明:通过解析JSON信息更逊省信息
* @param JsonData JSON数据 {"10101": "北京","10102": "上海"}
*/
public void UpdateProvinceData(String JsonData){
runOnUiThread(() -> {
try {
//进行JSON数据的反序列化
JSONObject param = JSONObject.parseObject(JsonData);
//将ProvinceID编号提取出来
Set<String> provinceKey = param.keySet();
Toast.makeText(MainActivity.this,"JSON省信息获取成功",Toast.LENGTH_SHORT).show();
for(String key:provinceKey){
//将数据以key:provinceName存储至provinceList
String provinceName = param.getString(key);
provinceMap.put(provinceName,key);
provinceArray.add(provinceName);
}
//进行适配器的操作
ArrayAdapter arrayAdapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_dropdown_item_1line, provinceArray);
spinner_prov.setAdapter(arrayAdapter);
} catch (JSONException e) {
Toast.makeText(MainActivity.this,"JSON解析失败",Toast.LENGTH_SHORT).show();
}
});
}
/**
* 名称:组件初始化函数
* 参数:无
* 返回值:无
*/
public void initView(){
spinner_city = findViewById(R.id.spiner1_city);
spinner_prov = findViewById(R.id.spiner1_province);
textView = findViewById(R.id.tv_weather);
linearLayout = findViewById(R.id.bg_control);
//设定spinner_prov的点击事件
spinner_prov.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//获取比如 "北京"的省名称,并获得于此对应的ProvinceID
provinceId = provinceMap.get(parent.getItemAtPosition(position).toString());
getHTTPLink(searchCityURI+provinceId+searchCityURI_suffix,1);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
spinner_city.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//获取比如 "北京"的市区名称,并获得于此对应的CityID
String cityName = parent.getItemAtPosition(position).toString();
//直辖市以及非直辖市的获取区别需要重视
cityID = cityMap.get(cityName);
//通过拼接的方式进行所在市区的天气查询
//直辖市=searchWeatherURI+"01"+cityID+searchWeatherURI_suffix
//非直辖市=searchWeatherURI+cityID+"01"+searchWeatherURI_suffix
String link = "";
if(cityName.equals("北京")||cityName.equals("上海")||cityName.equals("重庆")||cityName.equals("天津")){
link = searchWeatherURI+provinceId+"01"+cityID+searchWeatherURI_suffix;
getHTTPLink(link,2);
}else{
link=searchWeatherURI+provinceId+cityID+"01"+searchWeatherURI_suffix;
getHTTPLink(link,2);
}
Toast.makeText(MainActivity.this,link,Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
}
}
工具类:
package com.example.mooc25hw;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* create by zy on 08/25/19
*/
public class FastJsonUtil {
//take json format data to java bean
public static <T> T toBean(String jsonData, Class<T> tClass) {
return JSON.parseObject(jsonData, tClass);
}
//take arbitrary object to json format data
public static String ToJson(Object obj) {
return JSON.toJSONString(obj);
}
//get json string one of single values
public static Object getSingleValue(String jsonData, String key) {
JSONObject jsonObject = JSONObject.parseObject(jsonData);
return jsonObject.get(key);
}
//get multiple values in the json string
public static List<String> getValue(String jsonData, String...key){
List<String> jsonList = new ArrayList<>();
JSONObject jsonObject = JSONObject.parseObject(jsonData);
for (int i = 0; i < key.length; i ++){
jsonList.add(String.valueOf(jsonObject.get(key[i])));
}
return jsonList;
}
//take json string to Java List Array
public static <T> List<T> toList(String jsonData, Class<T> tClass) {
return JSON.parseArray(jsonData, tClass);
}
//take json string to Java Map Array
public static Map<String, Object> toMap(String jsonData){
return JSON.parseObject(jsonData,new TypeReference<Map<String, Object>>(){});
}
//take json string to List<Map<String,Object>> Array
public static List<Map<String, Object>> toListMap(String jsonData){
return JSON.parseObject(jsonData,new TypeReference<List<Map<String, Object>>>(){});
}
}
package com.example.mooc25hw;
import android.util.Log;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* create by zy on 08/22/2019
* consult zhy OkHttpUtils
*/
public class OkHttpUtils {
private volatile static OkHttpUtils mInstance;
private OkHttpClient mokHttpClient;
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
public OkHttpUtils(OkHttpClient okHttpClient) {
if (okHttpClient == null) {
mokHttpClient = new OkHttpClient();
} else {
mokHttpClient = okHttpClient;
}
}
public static OkHttpUtils initClient(OkHttpClient okHttpClient) {
if (mInstance == null) {
synchronized (OkHttpUtils.class) {
if (mInstance == null) {
mInstance = new OkHttpUtils(okHttpClient);
}
}
}
return mInstance;
}
public static OkHttpUtils getInstance() {
return initClient(null);
}
/**
* Get Request
*
* @param getUrl request url(get)
* @param callback callback interface by okHttp
*/
public void getRequest(String getUrl, final Callback callback) {
new Thread(() -> {
Request request = new Request.Builder()
.url(getUrl)
.build();
mokHttpClient.newCall(request).enqueue(callback);
}).start();
}
/**
* POST Request
*
* @param data afferent json data we can be afferent Map List pojo...
* @param postUrl request url(post)
* @param callback callback interface by okHttp
*/
public void postRequest(final Object data, String postUrl, final Callback callback) {
new Thread(() -> {
String json = FastJsonUtil.ToJson(data);
Log.d("Send Json -->",json);
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(postUrl)
.post(body)
.build();
mokHttpClient.newCall(request).enqueue(callback);
}).start();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bg_control"
android:orientation="vertical"
android:background="@color/sun"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:paddingTop="20dp"
android:paddingLeft="20dp"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:paddingLeft="20dp"
android:layout_weight="1"
android:id="@+id/spiner1_province"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Spinner
android:layout_weight="1"
android:paddingLeft="20dp"
android:id="@+id/spiner1_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Spinner>
</LinearLayout>
<TextView
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:text="天气"
android:textSize="30dp"
android:id="@+id/tv_weather"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>