1.查询天气功能:通过输入地区的adcode码(后续有机会做一下通过输入地区)查询天气情况;
2.添加关注功能:对于某个县级行政区域,可以在其天气界面点击关注,之后在“我的关注”里可以进行查看;
3.区域选择功能:省、市、县三级行政区域可以分层点击,在县级行政区域点击之后,跳转到此地区的天气情况界面。
4.界面展示:
(1)主界面:
(2)“我的关注”界面:
(3)天气情况界面:
1.对于天气预报APP,首先需要思考清楚需要几个Activity或者碎片:
(1)主页面(MainActivity):在主页面需要放置搜索输入框、搜索按钮,以及三级行政区域,而三级行政区域是靠碎片来实现的;
(2)碎片(ChooseAreaFragment):碎片定义包含了三级行政区域列表,上一级行政区域点击会进入到下一级,最后一级行政区域点击会跳转到此地的天气情况界面;
(3)天气情况界面(WeatherActivity):通过查询或者三级列表点击,进入到天气情况界面,包含温度、湿度、当前时间等信息,以及“关注”按钮、“取消关注”按钮、“返回主页面”按钮、“刷新”按钮;
(4)关注列表界面(MyConcernList):这个界面是所关注的城市,通过点击列表的城市,进入到所对应的天气情况界面。
在某个城市的天气显示情况界面,点击关注之后,需要如何对信息进行存储,以及如何在关注列表点击此城市后跳转到对应的天气界面呢?
1.对于关注的城市,信息存储包含两个内容:城市的名称(用于在关注界面进行显示)和城市的adcode(用于点击之后跳转到天气情况界面),清楚需要存储的信息之后,建立对应的数据库和表(新建一个java类,继承SQLiteOpenHelper):
public class MyDBhelper extends SQLiteOpenHelper {
public static final String DB_NAME="Concern3.db";
public static final int VERSION=1;
public static final String TABLE_NAME="Concern";
//对数据库进行创建
public static final String CREATE_CONCERN = "create table Concern("
+ "city_code String primary key not null,"
+ "city_name String not null)";
private Context mContext;
public MyDBhelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
super(context,name,factory,version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db){
db.execSQL(CREATE_CONCERN);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
}
}
2.有了数据库和表,接下来便是如何在点击“关注”时将城市名称和idcode收集、存储到数据库中。需要在天气情况界面添加一个“关注”按钮,而这个按钮的点击响应事件为:
concern.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyDBhelper dbHelper = new MyDBhelper(WeatherActivity.this, DB_NAME, null, 1);
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("city_code", countyCode);
values.put("city_name", countyName);
db.insert(TABLE_NAME, null, values);
Toast.makeText(WeatherActivity.this, "关注成功!", Toast.LENGTH_LONG).show();
}
});
可以看到,关注的点击响应事件会将countyCode和countyName这两个参数传递并存储到数据库,而对应“关注”按钮应该也有“取消关注”按钮,这个按钮的点击响应事件为:
concealConcern.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyDBhelper dbHelper = new MyDBhelper(WeatherActivity.this, DB_NAME, null, 1);
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete(TABLE_NAME,"city_code=?",new String[]{
String.valueOf(countyCode)});
Toast.makeText(WeatherActivity.this, "取消关注成功!", Toast.LENGTH_LONG).show();
}
});
在存储完成后,需要一个新的Activity(MyConcernList)来显示所有的关注城市,MyConcernList中包含了所有关注城市的名称并显示出来,在此给出从数据库填装数据的代码:
private void InitConcern() {
//进行数据填装
MyDBhelper dbHelper = new MyDBhelper(this,DB_NAME,null,1);
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.rawQuery("select * from Concern",null);
while(cursor.moveToNext()){
String city_code = cursor.getString(cursor.getColumnIndex("city_code"));
String city_name = cursor.getString(cursor.getColumnIndex("city_name"));
city_codeList.add(city_code);
city_nameList.add(city_name);
}
}
1.有了adcode码,便可以通过高德的API来进行地区天气情况的查询。高德的API需要去申请,高德API申请的地址如下:
高德API
注意:选择注册的为Web服务,天气查询和行政区域查询都可以用这个key值。
有了API之后,访问天气的链接为:
String weatherUrl = "https://restapi.amap.com/v3/weather/weatherInfo?city=" + adCode + "&key=c1894e9fcaf35e9fceabe9afaf40d45f";
其中key值为申请所得,前面的格式是固定的。访问行政区域的链接为:
String address = "https://restapi.amap.com/v3/config/district?keywords="+provinceName+"&subdistrict=1&key=c1894e9fcaf35e9fceabe9afaf40d45f";
其中key值同上边的key值,在链接中的“subdistrict=1”表示的含义为返回下一级行政区域,如果“subdistrict=2”则返回下两级行政区域。
2.不论天气还是行政区域查询,返回的都是json数据,要想得到最后的显示:天气情况或者行政区域,都需要对json数据进行解析,json数据解析参考下面这篇文章:
使用Gson解析常见字符串
需要注意的是,只有用[ ]方括号括起来的数据,才可以看作是一个数组,在数组外的字符串解析同整个数组是同级的,而[ ]方括号内的数据是同级的(低一级别),在此列出天气和行政区域的json数据解析。
(1)天气查询返回的json数据解析:
public static Weather handleWeatherResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray jsonArray = jsonObject.getJSONArray("lives");
for(int i=0; i<jsonArray.length(); i++){
JSONObject x = jsonArray.getJSONObject(i);
String weatherContent = x.toString();
return new Gson().fromJson(weatherContent, Weather.class);
} }catch (Exception e) {
e.printStackTrace();
}
return null;
}
(2)行政区域查询返回的json数据解析(此处返回的仅含下一级行政区域):
a.省级数据解析:
public static boolean handleProvinceResponse(String response){
if (!TextUtils.isEmpty(response)) {
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray countryAll = jsonObject.getJSONArray("districts");
for (int i = 0; i < countryAll.length(); i++) {
JSONObject countryLeve0 = countryAll.getJSONObject(i);
//插入省
JSONArray provinceAll = countryLeve0.getJSONArray("districts");
for (int j = 0; j < provinceAll.length(); j++) {
JSONObject province1 = provinceAll.getJSONObject(j);
String adcode1 = province1.getString("adcode");
String name1 = province1.getString("name");
Province provinceN = new Province();
provinceN.setProvinceCode(adcode1);
provinceN.setProvinceName(name1);
provinceN.save();
}
return true;
}
}
catch(JSONException e){
e.printStackTrace();
}
}
return false;
}
b.市级数据解析:
public static boolean handleCityResponse(String response, String provinceCode){
if (!TextUtils.isEmpty(response)) {
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray provinceAll = jsonObject.getJSONArray("districts");
for (int i = 0; i < provinceAll.length(); i++) {
JSONObject province1 = provinceAll.getJSONObject(i);
//插入市
JSONArray cityAll = province1.getJSONArray("districts");
for (int j = 0; j < cityAll.length(); j++) {
JSONObject city2 = cityAll.getJSONObject(j);
String adcode2 = city2.getString("adcode");
String name2 = city2.getString("name");
City cityN = new City();
cityN.setCityCode(adcode2);
cityN.setCityName(name2);
cityN.setProvinceCode(provinceCode);
cityN.save();
}
return true;
}
}
catch(JSONException e){
e.printStackTrace();
}
}
return false;
}
c.县级数据解析:
public static boolean handleCountyResponse(String response, String cityCode){
if (!TextUtils.isEmpty(response)) {
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray cityAll = jsonObject.getJSONArray("districts");
for (int i = 0; i < cityAll.length(); i++) {
JSONObject city2 = cityAll.getJSONObject(i);
//插入市
JSONArray countyAll = city2.getJSONArray("districts");
for (int j = 0; j < countyAll.length(); j++) {
JSONObject county3 = countyAll.getJSONObject(j);
String adcode3 = county3.getString("adcode");
String name3 = county3.getString("name");
County countyN = new County();
countyN.setCountyCode(adcode3);
countyN.setCountyName(name3);
countyN.setCityCode(cityCode);
countyN.save();
}
return true;
}
}
catch(JSONException e){
e.printStackTrace();
}
}
return false;
}
天气预报APP需要进行网络请求,需要进行联网。此处用到的是okHttp,新建一个java类,命名为HttpUtil,代码如下:
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class HttpUtil {
public static void sendOkHttpRequest(final String address, okhttp3.Callback callback){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(address).get().build();
client.newCall(request).enqueue(callback);
}
}
在AndroidManifest.xml中进行权限获取:
android:name="android.permission.INTERNET" />
android:name="android.permission.ACCESS_NETWORK_STATE" />
天气预报APP的整体框架是郭霖《第一行代码》的第十四章"酷派天气",感兴趣可以去具体看一下。最后,附上天气预报APP的github地址,代码仍不完善,欢迎指正。
Weather