前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >安卓天气预报app(四)——搜索城市、完善页面

安卓天气预报app(四)——搜索城市、完善页面

作者头像
roydonGuo
发布2022-11-02 15:44:24
1.7K0
发布2022-11-02 15:44:24
举报
文章被收录于专栏:posts

简易的安卓天气app(一)——解析Json数据、数据类封装简易的安卓天气app(二)——适配器、每小时数据展示简易的安卓天气app(三)——城市管理、数据库操作 📌简易的安卓天气app(四)——搜索城市、完善页面

需求🏷️

前三篇重要的功能已经实现的差不多了,完成了api获取数据,封装数据,展示数据,和一些数据库操作,接着就是按照自己的意愿,搜索城市,查看此城市天气,并决定将此城市加入数据库操作,方法是尽可能地简化的,本次项目共涉及三个页面之间的跳转,逻辑清晰,条理明朗,后续更多复杂化操作,和更多重复性操作有待探索,本质上都是已有代码的延申。 先获取到全国各个城市的信息,展示在搜索城市页面,方便查找。 实现步骤:

  1. AutoCompleteTextView输入提示文本框
  2. 实现读取全部城市展示
  3. 搜索框搜索指定城市

实现效果:

gif
gif

涉及内容

  • AutoCompleteTextView输入提示文本框
  • 文件读取,Json数据封装,RecyclerView数据展示
  • 根据城市名称刷新天气

项目结构

2
2

此文为项目开发第四篇文章,故前面文章已经讲完一部分内容,想了解详细步骤移步页首,每一篇文章都已经给出独立源码,可自行根据需要模拟;;

界面设计

搜索页面设计:

4
4

大概就是三层的线性布局: 第一层TextView接受主页面传进来的当前天气的城市名称。 第二层就是一个搜索框,使用到的是AutoCompleteTextView,带有提示信息的输入框,EditView也可以,右边搜索图标设置点击监听事件。 第三层就是一个RecyclerView展示全部城市名称,也可以设置点击事件监听,或者输入框输入,此处作为提示出现,都是可行方案

搜索城市页面输入框代码: 输入框AutoCompleteTextView此次用到的属性:

代码语言:javascript
复制
android:completionThreshold="1" //输入一个字符就给出提示
android:dropDownHorizontalOffset://提示菜单与文本起始的水平间距
android:dropDownHeight://设置提示框的高度,太小可能会遮盖部分提示,不过可以上下滚动显示
android:dropDownWidth://设置提示框的宽度,太小可能会遮盖部分提示
android:imeOptions="actionSearch" //键盘点击搜索事件
代码语言:javascript
复制
<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>

输入城市显示提示文本:

5
5

省、城市数据类封装

既然需求中需要输入一个字就给出相关城市提示信息,那么就要有全部城市数据,才能以此为根据提示城市名称,由于获取全国全部城市的API太难找,而且免费版的还有使用上限,所以此处直接根据文件读取,并封装。前几篇文章由于网络请求api封装用到的是Gson第三方工具,也提到了使用JsonObject等封装,所以此文会使用JsonArray,JsonObject来进行Json数据封装。 给出的City.txt文件放在main文件夹下的assets文件夹(res同级)下

6
6

里面给出的就是全国省份,各省下辖市,以及市下的区和县。 例如:

代码语言:javascript
复制
[{
		"pname": "北京市",
		"city": [{
			"name": "北京市",
			"area": [
				"东城区",
				"西城区",
				.........
			]
		}]
	},
	{
		"pname": "天津市",
		"city": [{
			"name": "天津市",
			"area": [
				"和平区",
				"河东区",
				.........
			]
		}]
	},
	{
		"pname": "河北省",
		"city": [{
				"name": "石家庄市",
				"area": [
					"长安区",
					"桥西区",
					.........
				]
			},
			{
				"name": "唐山市",
				"area": [
					"路北区",
					"路南区",
					.........
				]
			},
			{
				"name": "秦皇岛市",
				"area": [
					"海港区",
					"山海关区",
					.........
				]
			},
			.........

前往下面地址自取(永久有效): 1.百度网盘:City.txt

  • 提取码:mnmp

2.阿里云盘:City.txt

  • 提取码:su08

前往json在线解析网站观察此文件结构

  1. 先观察省,此处就以北京和天津为例,前面文章也提到了观察法,先是一个中括号”[“括住了全部省份,每个省份都是一个Object对象(大括号”{“阔了起来)。
  2. 再观察市,每个省里面的city属性名表示此省下辖的市,也是”[“包裹起来,表示是个数组,里面包含了省下全部市的信息(包括name市名称,area数组:区/县),这里就解析到各个城市,因为套法一样。
6
6

接着就是封装(连带着省份也一起封装吧),需要两个数据类,因为只封装到各个省下的城市。

7
7

ProvinceBean封装省份名称pname和市city,市是集合List

代码语言:javascript
复制
public class ProvinceBean {//省
    private String pname;
    private List<CityBean> city;
	//set、get、toString、构造。。。。。。
}

CityBean封装市名称name和区/县area,县是数组String[]

代码语言:javascript
复制
public class CityBean {
    private String name;
    private String[] area;//县/区
    private String tem;//方便数据库操作
    private String updateTime;//方便数据库操作
    //set、get、toString、构造。。。。。。

Json数据解析

接着就是从文件City.txt读取信息 下面是读取方法

代码语言:javascript
复制
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有空格和换行,这样的格式很正常):

7
7

然后得到了一个字符串resultCity ,接着就是根据resultCity 进行数据封装

代码语言:javascript
复制
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);
	//添加市,也可以用省里封装的List<CityBean>,直接set,以后不免用到CityBean,就1并封装了
	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[]数组。

8
8

现在,先在SelectCityActivity.java中定义AutoCompleteTextView; private AutoCompleteTextView query; 然后绑定组件

代码语言:javascript
复制
query = (AutoCompleteTextView) findViewById(R.id.edit_query);

接着就是设置一个ArrayAdapter适配器,里面设置样式为android.R.layout.simple_list_item_1安卓提供的样式,就是简单的白框, 然后传入城市的数组; 在此之前,这个城市数组还得定义好,前面Json数据解析我们已经知道,从City.txt文件已经拿到了全部城市,并成功传值给mCityBeanList

72
72

然后我们把这个List转成String数组,放在适配器ArrayAdapter中;

代码语言:javascript
复制
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查询城市时传入的城市名称不能带市,只能北京,天津,上海,不可北京市,上海市。 然后适配器就可以传参了,如下:

代码语言:javascript
复制
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, cityArray);
query.setAdapter(adapter);//

然后添加输入框右面的搜索图标的点击事件,输入框的城市名就传到了主页并显示天气

代码语言:javascript
复制
	ivSearch = (ImageView) findViewById(R.id.iv_search);
        ivSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            //拿到输入框的值
                String cityName = query.getText().toString();
//                ToastUtil.showLongToast(SelectCityActivity.this, cityName);
                Intent intent = new Intent(SelectCityActivity.this, MainActivity.class);
                intent.putExtra("inputCity", cityName);
                setResult(200, intent);
                finish();
            }
        });

城市显示

用到RecyclerView,只要把此类中已经封装好的全部城市集合传进去就行了,前面文章已经讲述过RecyclerView的用法,同时,也可以实现点击item跳转到主页获取天气的操作,前面文章也已经提到,不想过多赘述了;

若是嫌弃城市列表太多,都显示在一个页面还得滑动屏幕一个个找; 那么,二级RecyclerView不妨考虑一下:先显示全部省,点击省时,弹出市; 亦或者Spinner、两个RecyclerView联动,等等方法;==(源码已给,自行探索)==

9
9

>实现上图的效果,用到了左右两个RecyclerView,点击左面,就对应显示有点数据,把数据解析那一步改改就行,省和市完整封装在一起,省不止要serPname了,还要把此省的全部市封装一下provinceBean.setCity(mCityBeanList); 下面是封装部分的源码,都是ArrayList,查询速度快。 为了保证搜索框还有提示功能,重新定义actureCityBeanList,传入集合actureCityBeanList转的数组;;

代码语言:javascript
复制
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);
//                    mCityNameList.add(cityName);
                    mCityBeanList.add(cityBean);
                    actureCityBeanList.add(cityBean);
                }
                provinceBean.setCity(mCityBeanList);
                mProvinceBeanList.add(provinceBean);

                // 添加县待拓展。。。。。。。。。
            }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求🏷️
  • 涉及内容
  • 项目结构
  • 界面设计
  • 省、城市数据类封装
  • Json数据解析
  • SelectCityActivity.java
    • 搜索框
      • 城市显示
      相关产品与服务
      数据库
      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档