网络知识 娱乐 Android天气预报丨极简版

Android天气预报丨极简版

【Android】实现天气预报·视频

首先说明,这是极简版,简单到无可附加!只写天气预报原理。

原理其实就是
【请求】 中国的、较完整的 天气预报网络接口,
【解析】返回的 JSON 数据,
【呈现】数据。
仅此而已,别无其他。

源码下载

WeatherDemo

实现效果图

天气预报极简版实现效果图

前提

请先阅读我在Bilibili发表的两篇文章:

《【Android】XHttp2网络请求库的源码浅析与快速上手总结》

《解决XHTTP2 自定义引入不方便的问题》

接下来我会默认你已经熟读了我上述两篇文章,然后开始天气预报原理的简单叙述

正文

首先是开发工具版本相关:

我用的是 AndroidStudio 4.2.2 + gradle-7.0.2-bin.zip + gradle:4.1.0

然后是依赖引入:

引入两个模块到工程

	include ':library-EditSpiner',':library-xhttp2'

引入两个模块到工程

接下来我们依赖两个模块到项目app的dependencies

    implementation project(":library-xhttp2")
    implementation project(":library-EditSpiner")

依赖两个模块到项目app的dependencies

sync一下gradle即可

接下来我们看一下源码的左侧目录树的样子

左侧目录树的样子

其中

Bean已经在 《【Android】XHttp2网络请求库的源码浅析与快速上手总结》 里面介绍过了,就是根据接口返回值一一对应创建的 JavaBean,又叫 Entity,又叫 POJO

DoubleClickHelper就是一个单纯的预防短时间内多次点击的帮助类,接口完全免费,大家不要无节制的请求,无需多言。

MyApplication里面是进行XHttp2的初始化,也不用多说。

主要是看一下 MainActivity

因为 Xhttp2 的自动解析功能过于强大,因此代码属实不长,我直接粘贴到下面

public class MainActivity extends AppCompatActivity {
    private String cityName;
    private TextView tvContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1、读取 XML 数据
        String[] cityCodeArray = getResources().getStringArray(R.array.cityCode);

        String[] cityNameArray = getResources().getStringArray(R.array.cityName);
        List<String> cityNameList = Arrays.asList(cityNameArray);

        // 2、将数据填充给 Spinner 控件
        EditSpinner editSpinner = findViewById(R.id.editspinner);
        editSpinner.setItemData(cityNameList);

        tvContent = findViewById(R.id.tv_content);
        Button btSearch = findViewById(R.id.bt_search);

        // 3、按钮点击响应事件
        btSearch.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // 防止快速点击
                if (DoubleClickHelper.isOnDoubleClick(3000)) {
                    Toast.makeText(MainActivity.this, "查询太频繁,手别抖!", Toast.LENGTH_SHORT).show();
                    return;
                }
                cityName = editSpinner.getText();
                if (TextUtils.isEmpty(cityName)) {
                    return;
                }
                // 根据填写的城市名称查到对应的城市代码
                int index = cityNameList.indexOf(cityName);
                if (index == -1) {
                    Toast.makeText(MainActivity.this, "输入城市名称未录入系统,请重新输入", Toast.LENGTH_SHORT).show();
                    return;
                }

                String url = "http://t.weather.itboy.net/api/weather/city/" + cityCodeArray[index];
                // Xhttp2 请求网络
                XHttp.get(url)
                        .syncRequest(false)
                        .execute(new CallBackProxy<Body<ResponseData>, ResponseData>(
                                new SimpleCallBack<ResponseData>() {
                                    @Override
                                    public void onSuccess(ResponseData data) {
                                        // 处理 data
                                        dealWithData(data);
                                    }

                                    @Override
                                    public void onError(ApiException e) {
                                        // 处理异常
                                    }
                                }
                        ) {});//最后一定要有 {} 否则解析失败

            }
        });
    }

    @SuppressLint("SetTextI18n")
    private void dealWithData(ResponseData data) {
        if (data == null) {
            Toast.makeText(MainActivity.this, "结果异常!", Toast.LENGTH_SHORT).show();
            return;
        }
        Forecast todayForecast = data.getForecast().get(0);
        String today = buildForecastString(todayForecast, "今天");

        Forecast tomorrowForecast = data.getForecast().get(1);
        String tomorrow = buildForecastString(tomorrowForecast, "明天");

        tvContent.setText(today + tomorrow);
    }

    private String buildForecastString(Forecast forecast, String day) {
        return day + "【" + cityName + "】天气" + "n" +
                "日期:" + forecast.getYmd() + "丨" +
                forecast.getWeek() + ";n" +
                "天气类型:" + forecast.getType() + ";n" +
                "最高温度:" + forecast.getHigh() + ";n" +
                "最低温度:" + forecast.getLow() + ";n" +
                "刮风情况:" + forecast.getFl() + "丨" +
                forecast.getFx() +
                ";n" +
                "天气建议:" + forecast.getNotice() + ";nnn";
    }

}

当然,其中还涉及到了Android中的Xml引入Array的方法、以及允许调用Http请求的XML配置。简单提一下吧

Android中的Xml引入Array的方法

因为我们要选择全国的 市、区、县 进行天气预报,比如 【北京市】,但是这个是汉字,天气预报接口需要的是对应的城市编码,比如北京市是101010100,详情对应请看:《天气预报调用接口》

String url = "http://t.weather.itboy.net/api/weather/city/" + 城市编码;

怎样做既能知道 城市名称,又能知道 城市编码?最好的是有一个现成的 JSON,每个都城市名称和编码都对应好,然后我们读取 JSON 数据,转成一个一个得对象即可。但是,就当前这个天气接口来说,我没有发现这个写好的 JSON。而是只有【101010100=北京】这样的对应方式。看到这样的对应方式,我直观的想法就是造两个数组,一个数组存 城市编码,另一个数组存 城市名称。这样我们选好城市名称后就能根据城市名称的 index 去查找到对应的 城市编码。于是就有了 res/values/arrays.xml 里面的两个数组了。为了避免影响本文阅读,我把它俩粘贴到文章的最后面。我查了一下,有台湾,大家可以放心使用,确实是中国的、较完整的接口。
请添加图片描述

Android允许调用Http请求的XML配置

Android SDK24 起,不再默认允许调用 HTTP 请求网络,如果的确需要请求 HTTP 网络,需要特殊在 AndroidManifest.xml 里进行特殊声明,如下图所示:

请添加图片描述

配置内容在 res/xml/network_security_config.xml 里面,具体内容如下


<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

请添加图片描述

经过这样配置,我们就可以在 Android SDK 大于 24 的时候 访问 HTTP 链接了。

结尾

代码过于简洁,我想我做到这里也就足够了,毕竟师傅领进门了,给你留点进步的空间。正所谓,十分能力使三分,留作七分给后人!

【参考文章】

《天气预报调用接口》

两个数组

<resources>
    <string-array name="cityCode">
        <item>101010100</item>
        <item>101010200</item>
        <item>101010300</item>
        <item>101010400</item>
        <item>101010500</item>
        <item>101010600</item>
        <item>101010700</item>
        <item>101010800</item>
        <item>101010900</item>
        <item>101011000</item>
        <item>101011100</item>
        <item>101011200</item>
        <item>101011300</item>
        <item>101011400</item>
        <item>101011500</item>
        <item>101011600</item>
        <item>101011700</item>
        <item>101011800</item>
        <item>101011900</item>
        <item>101012000</item>
        <item>101012100</item>
        <item>101020100</item>
        <item>101020200</item>
        <item>101020300</item>
        <item>101020400</item>
        <item>101020500</item>
        <item>101020600</item>
        <item>101020700</item>
        <item>101020800</item>
        <item>101020900</item>
        <item>101021000</item>
        <item>101021100</item>
        <item>101021101</item>
        <item>101021102</item>
        <item>101021200</item>
        <item>101021300</item>
        <item>101030100</item>
        <item>101030200</item>
        <item>101030300</item>
        <item>101030400</item>
        <item>101030500</item>
        <item>101030600</item>
        <item>101030700</item>
        <item>101030800</item>
        <item>101030900</item>
        <item>101031000</item>
        <item>101031100</item>
        <item>101031200</item>
        <item>101031300</item>
        <item>101031400</item>
        <item>101040100</item>
        <item>101040200</item>
        <item>101040300</item>
        <item>101040400</item>
        <item>101040500</item>
        <item>101040600</item>
        <item>101040700</item>
        <item>101040800</item>
        <item>101040900</item>
        <item>101041000</item>
        <item>101041100</item>
        <item>101041200</item>
        <item>101041300</item>
        <item>101041400</item>
        <item>101041500</item>
        <item>101041600</item>
        <item>101041700</item>
        <item>101041800</item>
        <item>101041900</item>
        <item>101042000</item>
        <item>101042100</item>
        <item>101042200</item>
        <item>101042300</item>
        <item>101042400</item>
        <item>101042500</item>
        <item>101042600</item>
        <item>101042700</item>
        <item>101042800</item>
        <item>101042900</item>
        <item>101043000</item>
        <item>101043100</item>
        <item>101043200</item>
        <item>101043300</item>
        <item>101043400</item>
        <item>101043500</item>
        <item>101043600</item>
        <item>101043700</item>
        <item>101050101</item>
        <item>101050102</item>
        <item>101050103</item>
        <item>101050104</item>
        <item>101050105</item>
        <item>101050106</item>
        <item>101050107</item>
        <item>101050108</item>
        <item>101050109</item>
        <item>101050110</item>
        <item>101050111</item>
        <item>101050112</item>
        <item>101050113</item>
        <item>101050201</item>
        <item>101050202</item>
        <item>101050203</item>
        <item>101050204</item>
        <item>101050205</item>
        <item>101050206</item>
        <item>101050207</item>
        <item>101050208</item>
        <item>101050209</item>
        <item>101050210</item>
        <item>101050301</item>
        <item>101050302</item>
        <item>101050303</item>
        <item