✅简易的安卓天气app(一)——解析Json数据、数据类封装
✅简易的安卓天气app(二)——适配器、每小时数据展示
✅简易的安卓天气app(三)——城市管理、数据库操作
📌简易的安卓天气app(四)——搜索城市、完善页面
需求🏷️
前三篇重要的功能已经实现的差不多了,完成了api获取数据,封装数据,展示数据,和一些数据库操作,接着就是按照自己的意愿,搜索城市,查看此城市天气,并决定将此城市加入数据库操作,方法是尽可能地简化的,本次项目共涉及三个页面之间的跳转,逻辑清晰,条理明朗,后续更多复杂化操作,和更多重复性操作有待探索,本质上都是已有代码的延申。
先获取到全国各个城市的信息,展示在搜索城市页面,方便查找。
实现步骤:
- AutoCompleteTextView输入提示文本框
- 实现读取全部城市展示
- 搜索框搜索指定城市
实现效果:
涉及内容
- AutoCompleteTextView输入提示文本框
- 文件读取,Json数据封装,RecyclerView数据展示
- 根据城市名称刷新天气
项目结构
此文为项目开发第四篇文章,故前面文章已经讲完一部分内容,想了解详细步骤移步页首,每一篇文章都已经给出独立源码,可自行根据需要模拟;;
界面设计
搜索页面设计:
大概就是三层的线性布局:
第一层TextView接受主页面传进来的当前天气的城市名称。
第二层就是一个搜索框,使用到的是AutoCompleteTextView,带有提示信息的输入框,EditView也可以,右边搜索图标设置点击监听事件。
第三层就是一个RecyclerView展示全部城市名称,也可以设置点击事件监听,或者输入框输入,此处作为提示出现,都是可行方案
搜索城市页面输入框代码:
输入框AutoCompleteTextView此次用到的属性:
1 2 3 4 5
| android:completionThreshold="1" //输入一个字符就给出提示 android:dropDownHorizontalOffset://提示菜单与文本起始的水平间距 android:dropDownHeight://设置提示框的高度,太小可能会遮盖部分提示,不过可以上下滚动显示 android:dropDownWidth://设置提示框的宽度,太小可能会遮盖部分提示 android:imeOptions="actionSearch" //键盘点击搜索事件
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <LinearLayout android:layout_width="380dp" android:layout_height="40dp" android:gravity="center_vertical" android:layout_gravity="center" android:background="@drawable/blackground"> <LinearLayout android:layout_width="0dp" android:layout_height="30dp" android:layout_marginRight="12dp" android:layout_weight="1" android:gravity="center_vertical" android:paddingLeft="12dp" android:paddingRight="12dp"> <AutoCompleteTextView android:id="@+id/edit_query" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:completionThreshold="1" android:dropDownHorizontalOffset="5dp" android:dropDownWidth="200dp" android:hint="请输入城市名称" android:imeOptions="actionSearch" android:paddingLeft="8dp" android:paddingRight="4dp" android:textColor="@color/white" android:singleLine="true" android:textSize="18sp" /> <ImageView android:layout_width="26dp" android:layout_height="26dp" android:id="@+id/iv_search" android:src="@mipmap/icon_search" /> </LinearLayout> </LinearLayout>
|
输入城市显示提示文本:
省、城市数据类封装
既然需求中需要输入一个字就给出相关城市提示信息,那么就要有全部城市数据,才能以此为根据提示城市名称,由于获取全国全部城市的API太难找,而且免费版的还有使用上限,所以此处直接根据文件读取,并封装。前几篇文章由于网络请求api封装用到的是Gson第三方工具,也提到了使用JsonObject等封装,所以此文会使用JsonArray,JsonObject来进行Json数据封装。
给出的City.txt文件放在main文件夹下的assets文件夹(res同级)下里面给出的就是全国省份,各省下辖市,以及市下的区和县。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| [{ "pname": "北京市", "city": [{ "name": "北京市", "area": [ "东城区", "西城区", ......... ] }] }, { "pname": "天津市", "city": [{ "name": "天津市", "area": [ "和平区", "河东区", ......... ] }] }, { "pname": "河北省", "city": [{ "name": "石家庄市", "area": [ "长安区", "桥西区", ......... ] }, { "name": "唐山市", "area": [ "路北区", "路南区", ......... ] }, { "name": "秦皇岛市", "area": [ "海港区", "山海关区", ......... ] }, .........
|
前往下面地址自取(永久有效):
1.百度网盘:City.txt
2.阿里云盘:City.txt
前往json在线解析网站观察此文件结构
- 先观察省,此处就以北京和天津为例,前面文章也提到了观察法,先是一个中括号”[“括住了全部省份,每个省份都是一个Object对象(大括号”{“阔了起来)。
- 再观察市,每个省里面的city属性名表示此省下辖的市,也是”[“包裹起来,表示是个数组,里面包含了省下全部市的信息(包括name市名称,area数组:区/县),这里就解析到各个城市,因为套法一样。
接着就是封装(连带着省份也一起封装吧),需要两个数据类,因为只封装到各个省下的城市。
ProvinceBean封装省份名称pname和市city,市是集合List
1 2 3 4 5
| public class ProvinceBean { private String pname; private List<CityBean> city; }
|
CityBean封装市名称name和区/县area,县是数组String[]
1 2 3 4 5 6
| public class CityBean { private String name; private String[] area; private String tem; private String updateTime;
|
Json数据解析
接着就是从文件City.txt读取信息
下面是读取方法
1 2 3 4 5 6 7 8 9 10
| InputStream inputStream = getResources().getAssets().open("City.txt"); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String lines = bufferedReader.readLine(); while (lines != null) { sb.append(lines); lines = bufferedReader.readLine(); } String resultCity = sb.toString(); Log.d("SelectCity", "resultCity>>>>>>>>>" + resultCity);
|
控制台显示(原txt有空格和换行,这样的格式很正常):然后得到了一个字符串resultCity ,接着就是根据resultCity 进行数据封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| JSONArray ja= new JSONArray(resultCity);
for (int i = 0; i < ja.length(); i++) { JSONObject provinceJsonObject = ja.getJSONObject(i); String provinceName = provinceJsonObject.getString("pname"); ProvinceBean provinceBean = new ProvinceBean(); provinceBean.setPname(provinceName); mProvinceBeanList.add(provinceBean); JSONArray cityArray = provinceJsonObject.getJSONArray("city"); for (int j = 0; j < cityArray.length(); j++) { JSONObject cityObj = cityArray.getJSONObject(j); String cityName = cityObj.getString("name"); CityBean cityBean = new CityBean(); cityBean.setName(cityName); mCityBeanList.add(cityBean); } } Log.d("SelectCity", "mCityBeanList>>>>>>>>>" + mCityBeanList.toString());
|
到此省和市的数据就已经封装好了,可以查看控制台,发现area=null,因为用不到此数据,就没有对area进行封装,需要封装直接cityBean.set完事,没有难度。
数据解析封装好,就是设置适配器,既然用到RecyclerView展示城市,那么城市的适配器就得写,毕竟封装好的城市数据不是String数组,是一个集合,为了规范(凑复杂度)直接写适配器吧(List转成String数组就可以不用写适配器,直接用ArrayAdapter)。
CityAdapter适配器:
前面文章提到过,略过过了就,,
指路==>适配器写法:
简易的安卓天气app(二)——适配器、每小时数据展示
根据目录适配器HourWeatherAdapter
索引
/**也可以在此适配器添加点击事件,拿到天气,此方法前面文章(根据目录适配器AddCityAdapter
索引)也提到过,此处略过/
SelectCityActivity.java
搜索框
搜索城市页面首先我们来设计搜索框输入文字提示框,首先,我们已经在xml布局中运用了AutoCompleteTextView,会自动根据输入的一个字匹配传入的值,有就显示提示,如下,这个弹出提示框其实是可以自定义样式的,这里就用默认了。后续会更新,适配器也是安卓提供的ArrayAdapter,传入的是全国所有市的String[]数组。
现在,先在SelectCityActivity.java中定义AutoCompleteTextView;
private AutoCompleteTextView query;
然后绑定组件
1
| query = (AutoCompleteTextView) findViewById(R.id.edit_query);
|
接着就是设置一个ArrayAdapter适配器,里面设置样式为android.R.layout.simple_list_item_1安卓提供的样式,就是简单的白框, 然后传入城市的数组;
在此之前,这个城市数组还得定义好,前面Json数据解析我们已经知道,从City.txt文件已经拿到了全部城市,并成功传值给mCityBeanList;
然后我们把这个List转成String数组,放在适配器ArrayAdapter中;
1 2 3 4 5
| String[] cityArray = new String[mCityBeanList.size()]; for (int i = 0; i < mCityBeanList.size(); i++) { cityArray[i] = mCityBeanList.get(i).getName().substring(0, mCityBeanList.get(i).getName().length() - 1); }
|
这里带了substring方法,主要是由于我们的天气api查询城市时传入的城市名称不能带市,只能北京,天津,上海,不可北京市,上海市。
然后适配器就可以传参了,如下:
1 2
| adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, cityArray); query.setAdapter(adapter);
|
然后添加输入框右面的搜索图标的点击事件,输入框的城市名就传到了主页并显示天气
1 2 3 4 5 6 7 8 9 10 11 12 13
| ivSearch = (ImageView) findViewById(R.id.iv_search); ivSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String cityName = query.getText().toString();
Intent intent = new Intent(SelectCityActivity.this, MainActivity.class); intent.putExtra("inputCity", cityName); setResult(200, intent); finish(); } });
|
城市显示
用到RecyclerView,只要把此类中已经封装好的全部城市集合传进去就行了,前面文章已经讲述过RecyclerView的用法,同时,也可以实现点击item跳转到主页获取天气的操作,前面文章也已经提到,不想过多赘述了;
若是嫌弃城市列表太多,都显示在一个页面还得滑动屏幕一个个找;
那么,二级RecyclerView不妨考虑一下:先显示全部省,点击省时,弹出市;
亦或者Spinner、两个RecyclerView联动,等等方法;==(源码已给,自行探索)==
>实现上图的效果,用到了左右两个RecyclerView,点击左面,就对应显示有点数据,把数据解析那一步改改就行,省和市完整封装在一起,省不止要serPname了,还要把此省的全部市封装一下provinceBean.setCity(mCityBeanList);
下面是封装部分的源码,都是ArrayList,查询速度快。
为了保证搜索框还有提示功能,重新定义actureCityBeanList,传入集合actureCityBeanList转的数组;;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| final JSONArray Data = new JSONArray(resultCity); for (int i = 0; i < Data.length(); i++) { JSONObject provinceJsonObject = Data.getJSONObject(i); String provinceName = provinceJsonObject.getString("pname"); ProvinceBean provinceBean = new ProvinceBean(); provinceBean.setPname(provinceName); mCityBeanList = new ArrayList<>(); final JSONArray cityArray = provinceJsonObject.getJSONArray("city"); for (int j = 0; j < cityArray.length(); j++) { JSONObject cityObj = cityArray.getJSONObject(j); String cityName = cityObj.getString("name"); CityBean cityBean = new CityBean(); cityBean.setName(cityName);
mCityBeanList.add(cityBean); actureCityBeanList.add(cityBean); } provinceBean.setCity(mCityBeanList); mProvinceBeanList.add(provinceBean);
}
|