『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区-51CTO.COM

『牛角书』鸿蒙HarmonyOS天气预报APP(一) 原创

roydon
发布于 2022-12-16 19:51
浏览
0收藏

1.准备工作

1.1创建项目

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

sdk为6版本,所以使用华为的远程模拟器p40即可。

1.2准备图片资源

这里把天气预报用到的天气提示的图片全放在资源目录下的media文件下。

具体资源在github仓库已包含,自行前往。

1.3配置文件

接着是修改配置文件,由于是发送网络请求请求api获取json天气数据,所以和安卓一样,需要修改配置文件,添加网络请求权限。

修改config.json,在module节点添加如下权限。

"reqPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  },
  {
    "name": "ohos.permission.GET_NETWORK_INFO"
  },
  {
    "name": "ohos.permission.SET_NETWORK_INFO"
  }
],

接着还要配置http协议的api也能正常访问。在module和app同级添加如下。

"deviceConfig": {
  "default": {
    "network": {
      "cleartextTraffic": true
    }
  }
},

如果想要去除页面顶部的标题区域,接着添加如下配置。

"metaData": {
  "customizeData": [
    {
      "name": "hwc-theme",
      "value": "androidhwext:style/Theme.Emui.NoTitleBar"
    }
  ]
}

最后添加完成预览:
『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

2.网络请求工具类

天气api以前文章提到过,自行前往–>简易的安卓天气app(一)——解析Json数据、数据类封装

最后得到的api形式为:

https://tianqiapi.com/api?version=v1&appid={your appid}&appsecret={your appsecret}

其中appid和appsecret需要自行申请,免费版有请求上限。

此api会默认根据访问者ip的地理位置进行定位。又因为虚拟手机没法获取设备位置且本人没有华为设备,没法加入定位功能,,所以暂时在代码中指定需要查询的城市,,,

2.1NetworkUtil

网络请求工具类,返回api获取的string字符串就行

public class NetworkUtil {
    /**
     * 18625561:27XjzrB7
     * 67342285:5XgTk31r
     * 19267789:Dhu3DShY
     */
    public static final String URL_WEATHER = "https://tianqiapi.com/api?version=v1&appid=67342285&appsecret=5XgTk31r";

    public static String httpGet(String cityName) {
        String urlGetJson = URL_WEATHER + "&city=" + cityName;
        StringBuilder sb = new StringBuilder();
        try {
            URL url = new URL(urlGetJson);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(10000);
            connection.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String temp;
            while ((temp = reader.readLine()) != null) {
                sb.append(temp);
            }
            reader.close();
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return sb.toString();
    }
}

2.2测试网络请求

为了保证主线程不受干扰,网络请求需要单独开辟一个异步线程请求数据。详情前往鸿蒙开发指南线程管理开发指导

创建一个异步线程:(使用lamda编程,不再new Runnable()实现 )

TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
globalTaskDispatcher.asyncDispatch(() -> {
	//网络请求,城市名指定即可,此处指定北京
    //String result = NetworkUtil.httpGet(cityName);
    String result = NetworkUtil.httpGet("北京");
    System.out.println(result);
}

成功获取数据:

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

2.3api返回数据结构

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

3.实体类封装

需要两个实体类封装数据:

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

WeatherBean封装城市名称更新时间即可,其中还包含DayWeatherBean的数组存放七天天气。

DayWeatherBean就是七天的每一天的天气详情,每天的天气还包含24小时详细天气,本文不再详细探讨。

public class WeatherBean implements Serializable {

    @SerializedName("cityid")
    private String cityid;

    private String city;//城市名称

    private String update_time;//更新时间

    private List<DayWeatherBean> data;//获取今日天气,get[0]
    // get set toString。。。
}
public class DayWeatherBean implements Serializable {
    @SerializedName("date")
    private String date;
    private String wea;//天气
    private String wea_img;//天气图标
    private String week;//周几
    private String tem;//温度
    //tv_tem_low_high=tem2+tem1拼接一起
    private String tem2;//低温
    private String tem1;//高温
    //tv_win=win+win_speed
    private String[] win;//风力
    private String win_speed;//风力等级
    //tv_air=air+air_level+air_tips拼接一起
    private String air;//
    private String air_level;//
    private String air_tips;//

//    @SerializedName("hours")
//    private List<HoursWeatherBean> hoursWeatherBeanList;

//    @SerializedName("index")
//    private List<TipsBean> mTipsBeans;

    // get set toString。。。
}

封装数据使用Google的gson进行

首先引入gson依赖:打开build.gradle添加依赖,别忘了右上角Sync Now同步一下

implementation 'com.google.code.gson:gson:2.8.5'

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

对获取到的数据result进行封装:得到WeatherBean对象

Gson gson = new Gson();
WeatherBean weatherBean = gson.fromJson(result, WeatherBean.class);

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

之后就是把数据渲染到ui上即可。

4.ui

4.1首页ui设计

使用StackLayout把壁纸放在最下面一层,接着就是DirectionalLayout布局。

使用ScrollView组件包裹DirectionalLayout可以使布局上下滑动,超出屏幕布局可滑动屏幕查看。

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

<?xml version="1.0" encoding="utf-8"?>
<StackLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent">

    <Image
        ohos:id="$+id:bg"
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:background_element="$media:bg"
        ohos:scale_mode="center"/>

    <DirectionalLayout
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:orientation="vertical">

        <!--头部bar-->
        <DirectionalLayout
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:bottom_margin="8vp"
            ohos:orientation="horizontal"
            ohos:top_margin="8vp">

            <Image
                ohos:id="$+id:add"
                ohos:height="30vp"
                ohos:width="30vp"
                ohos:image_src="$media:add"
                ohos:layout_alignment="horizontal_center"
                ohos:left_margin="20vp"
                ohos:scale_mode="clip_center"/>

            <Text
                ohos:id="$+id:text_city"
                ohos:height="match_parent"
                ohos:width="260vp"
                ohos:layout_alignment="horizontal_center"
                ohos:text="郑州"
                ohos:text_alignment="horizontal_center"
                ohos:text_color="#000"
                ohos:text_size="26fp"/>

            <Image
                ohos:id="$+id:more"
                ohos:height="30vp"
                ohos:width="30vp"
                ohos:image_src="$media:more"
                ohos:layout_alignment="horizontal_center"
                ohos:right_margin="20vp"
                ohos:scale_mode="clip_center"/>

        </DirectionalLayout>

        <!--分割白横线-->
        <ProgressBar
            ohos:id="$+id:progressbar"
            ohos:height="6vp"
            ohos:width="match_parent"
            ohos:max="100"
            ohos:min="0"
            ohos:progress="100"
            ohos:progress_element="#FFF6F0F0"
            ohos:progress_width="10vp"/>

        <ScrollView
            ohos:rebound_effect="true"
            ohos:height="match_parent"
            ohos:width="match_parent">

        <DirectionalLayout
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:layout_alignment="horizontal_center"
            ohos:orientation="vertical"
            ohos:top_margin="10vp"
            >
            <!--ohos:scale_mode="stretch"表示将原图缩放到与Image大小一致-->
            <Image
                ohos:id="$+id:weather_img"
                ohos:height="100vp"
                ohos:width="120vp"
                ohos:image_src="$media:weather_yin"
                ohos:layout_alignment="horizontal_center"
                ohos:scale_mode="stretch"/>

            <Text
                ohos:id="$+id:text_weather"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:layout_alignment="horizontal_center"
                ohos:text="阴转多云"
                ohos:text_color="#000"
                ohos:text_size="20fp"/>

            <Text
                ohos:id="$+id:text_tem"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:layout_alignment="horizontal_center"
                ohos:text="26°C"
                ohos:text_color="#000"
                ohos:text_size="50fp"/>

            <Text
                ohos:id="$+id:text_tem_low_high"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:layout_alignment="horizontal_center"
                ohos:text="20°C/30°C"
                ohos:text_color="#000"
                ohos:text_size="20fp"/>

            <Text
                ohos:id="$+id:text_week"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:layout_alignment="horizontal_center"
                ohos:text="星期日"
                ohos:text_color="#000"
                ohos:text_size="20fp"/>

            <!--风向以及出行建议-->
            <DirectionalLayout
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:left_margin="10vp"
                ohos:right_margin="10vp"
                ohos:top_padding="6vp"
                ohos:bottom_padding="6vp"
                ohos:background_element="$graphic:background_ability_main"
                ohos:layout_alignment="horizontal_center"
                ohos:orientation="horizontal"
                >
                <!--左侧-->
                <DirectionalLayout
                    ohos:height="match_content"
                    ohos:width="match_content"
                    ohos:left_margin="10vp"
                    ohos:right_margin="10vp"
                    ohos:layout_alignment="horizontal_center"
                    ohos:orientation="vertical"
                    >
                    <Image
                        ohos:height="60vp"
                        ohos:width="60vp"
                        ohos:image_src="$media:fengli"
                        ohos:layout_alignment="horizontal_center"
                        ohos:scale_mode="stretch"/>
                    <Text
                        ohos:id="$+id:text_win"
                        ohos:height="match_content"
                        ohos:width="match_content"
                        ohos:layout_alignment="horizontal_center"
                        ohos:text="西北风3~4级"
                        ohos:text_color="#fff"
                        ohos:text_size="16fp"/>
                </DirectionalLayout>
                <!--右侧-->
                <DirectionalLayout
                    ohos:height="match_content"
                    ohos:width="match_parent"
                    ohos:left_margin="10vp"
                    ohos:right_margin="10vp"
                    ohos:layout_alignment="horizontal_center"
                    ohos:orientation="vertical"
                    >
                    <!--空气质量-->
                    <DirectionalLayout
                        ohos:height="match_content"
                        ohos:width="match_parent"
                        ohos:orientation="horizontal"
                        ohos:layout_alignment="horizontal_center"
                        ohos:alignment="right"
                        >
                        <Image
                            ohos:height="30vp"
                            ohos:width="30vp"
                            ohos:image_src="$media:kongqi"
                            ohos:layout_alignment="horizontal_center"
                            ohos:scale_mode="stretch"/>
                        <Text
                            ohos:id="$+id:text_air"
                            ohos:height="match_content"
                            ohos:width="match_content"
                            ohos:layout_alignment="horizontal_center"
                            ohos:text="43 | 优"
                            ohos:text_color="#fff"
                            ohos:text_size="16fp"/>

                    </DirectionalLayout>

                    <!--出行建议-->
                    <DirectionalLayout
                        ohos:height="match_content"
                        ohos:width="match_parent"
                        >
                        <Text
                            ohos:id="$+id:text_tips"
                            ohos:height="match_parent"
                            ohos:width="match_content"
                            ohos:multiple_lines="true"
                            ohos:max_text_lines="3"
                            ohos:truncation_mode="ellipsis_at_end"
                            ohos:text="儿童、老年人及心脏病、呼吸系统疾病患者应尽量减少体力消耗大的户外活动。"
                            ohos:text_color="#FF9F02FF"
                            ohos:text_size="13fp"/>

                    </DirectionalLayout>

                </DirectionalLayout>

            </DirectionalLayout>

            <ListContainer
                ohos:id="$+id:list_container"
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:left_margin="10vp"
                ohos:right_margin="10vp"
                ohos:top_margin="10vp"
                ohos:top_padding="6vp"
                ohos:bottom_padding="6vp"
                ohos:background_element="$graphic:background_ability_main"
                ohos:layout_alignment="horizontal_center"
                ohos:orientation="vertical"/>

        </DirectionalLayout>
        </ScrollView>

    </DirectionalLayout>

</StackLayout>

还包含一个ListContainer渲染未来七天天气数据。list_item的模板设计:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="40vp"
    ohos:width="match_parent"
    ohos:left_margin="20vp"
    ohos:right_margin="20vp"
    ohos:orientation="horizontal"
    ohos:alignment="horizontal_center">
    <Text
        ohos:id="$+id:text_date"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:weight="1"
        ohos:text="11-11"
        ohos:text_color="#fff"
        ohos:text_size="20fp"/>
    <Image
        ohos:id="$+id:day_weather_img"
        ohos:height="30vp"
        ohos:width="30vp"
        ohos:weight="1"
        ohos:image_src="$media:weather_yin"
        ohos:scale_mode="inside"/>
    <Text
        ohos:id="$+id:text_tem_low_high"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text_alignment="right"
        ohos:weight="1"
        ohos:text="20°C/30°C"
        ohos:text_color="#fff"
        ohos:text_size="20fp"/>
</DirectionalLayout>

实现效果:

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

4.2获取图片位置工具

获取到api返回的数据后,把数据封装成实体类,其中wea_img属性接受一个string字符串,判断字符串的值返回对应图片资源的int整型。

package com.roydon.weatherforcast.utils;

import com.roydon.weatherforcast.ResourceTable;

public class WeatherImgUtil {

    public static int getImgResOfWeather(String weaStr) {
        int result = 0;
        switch (weaStr) {
            case "qing":
                result = ResourceTable.Media_weather_yin;
                break;
            case "yin":
                result = ResourceTable.Media_weather_yin;
                break;
            case "yu":
                result = ResourceTable.Media_weather_dayu;
                break;
            case "yun":
                result = ResourceTable.Media_weather_duoyun;
                break;
            case "bingbao":
                result = ResourceTable.Media_weather_leizhenyubingbao;
                break;
            case "wu":
                result = ResourceTable.Media_weather_wu;
                break;
            case "shachen":
                result = ResourceTable.Media_weather_shachenbao;
                break;
            case "lei":
                result = ResourceTable.Media_weather_leizhenyu;
                break;
            case "xue":
                result = ResourceTable.Media_weather_daxue;
                break;
            default:
                result = ResourceTable.Media_weather_qing;
                break;
        }
        return result;
    }
}

4.3渲染数据

组件全部注册好之后,封装一个渲染数据的方法。

public void dataShow(WeatherBean weatherBean) {
    if (weatherBean == null) {
        return;
    }
    city.setText(weatherBean.getCity());
    DayWeatherBean dayWeather = weatherBean.getData().get(0);//当天天气
    if (dayWeather == null) {
        return;
    }
    // 当天天气
    weatherImg.setPixelMap(WeatherImgUtil.getImgResOfWeather(dayWeather.getWea_img()));
    weather.setText(dayWeather.getWea());
    tem.setText(dayWeather.getTem());
    temLowHigh.setText(dayWeather.getTem2() + "/" + dayWeather.getTem1());
    week.setText(dayWeather.getWeek());
    win.setText(dayWeather.getWin()[0] + dayWeather.getWin_speed());
    air.setText(dayWeather.getAir() + " | " + dayWeather.getAir_level());
    tips.setText("👒:" + dayWeather.getAir_tips());
    // ListContainer展示未来七天天气
    List<DayWeatherBean> dayWeatherBeanList = weatherBean.getData();
    DayWeatherBeanProvider dayWeatherBeanProvider = new DayWeatherBeanProvider(dayWeatherBeanList, this);
    listContainer.setItemProvider(dayWeatherBeanProvider);
}

获取api数据与渲染ui独立封装一个方法:

其中渲染ui需要开辟ui异步线程getUITaskDispatcher().asyncDispatch()

/**
 * GlobalTaskDispatcher 派发异步任务
 */
public void getWeather(String cityName) {
    //网络请求
    TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
    globalTaskDispatcher.asyncDispatch(() -> {
        String result = NetworkUtil.httpGet(cityName);
        // System.out.println(result);
        Gson gson = new Gson();
        WeatherBean weatherBean = gson.fromJson(result, WeatherBean.class);
        if (weatherBean == null) {
            getUITaskDispatcher().asyncDispatch(() -> {
                ToastUtil.showToast(this, "貌似出了点问题~");
            });
        } else {
            System.out.println(weatherBean);
            getUITaskDispatcher().asyncDispatch(() -> {
                ToastUtil.showToast(this, weatherBean.getCity() + "天气更新");
                dataShow(weatherBean);
            });
        }
    });
}

4.3.1ListContainer

跟安卓一样,也是需要适配器。用来渲染哪个模板,并把数据渲染到模板。

①新建provider包,包中新建DayWeatherBeanProvider适配器继承自BaseItemProvider

②重写BaseItemProvider中的方法,鼠标悬停会提示。

 @Override
public int getCount() {
    return list == null ? 0 : list.size();
}

@Override
public Object getItem(int i) {
    if (list != null && i >= 0 && i < list.size()){
        return list.get(i);
    }
    return null;
}

@Override
public long getItemId(int i) {
    //可添加具体处理逻辑
    return i;
}

Override
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
    return null;
}

③添加数据集合List<DayWeatherBean>与AbilitySlice,并创建构造方法。

private List<DayWeatherBean> list;
private AbilitySlice slice;

public DayWeatherBeanProvider(List<DayWeatherBean> list, AbilitySlice slice) {
    this.list = list;
    this.slice = slice;
}

④重写getComponent(),渲染模板

@Override
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
    final Component cpt;
    if (component == null) {
        cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_day_weather, null, false);
    } else {
        cpt = component;
    }
    DayWeatherBean dayWeatherBean = list.get(i);
    Text date = (Text) cpt.findComponentById(ResourceTable.Id_text_date);
    Text tem = (Text) cpt.findComponentById(ResourceTable.Id_text_tem_low_high);
    Image image = (Image) cpt.findComponentById(ResourceTable.Id_day_weather_img);
    date.setText(dayWeatherBean.getDate().substring(5,10));
    image.setPixelMap(WeatherImgUtil.getImgResOfWeather(dayWeatherBean.getWea_img()));
    tem.setText(dayWeatherBean.getTem2() + "/" + dayWeatherBean.getTem1());
    return cpt;
}

如果非得想加入每小时天气数据展示,可前往简易的安卓天气app(二)——适配器、每小时数据展示
折线图设计可参考安卓WeatherForcast4
最终效果:

『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

4.4Toast封装

自定义Toast弹框鸿蒙开发指南也为我们提供好了。详情前往鸿蒙开发指南ToastDialog

此处封装一个带图片的Toast工具类。渲染时需要开辟ui异步线程。

getUITaskDispatcher().asyncDispatch(() -> {
     ToastUtil.showToast(this, weatherBean.getCity() + "天气更新");
 });

新建两个xml,一个是布局设置主要样式,一个是ui美化设置圆角与背景
『牛角书』鸿蒙HarmonyOS天气预报APP(一)-鸿蒙开发者社区

layout_toast.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_content"
    ohos:width="match_content"
    ohos:padding="10vp"
    ohos:background_element="$graphic:background_toast_element"
    ohos:orientation="horizontal">
    <Image
        ohos:width="16vp"
        ohos:height="16vp"
        ohos:scale_mode="inside"
        ohos:image_src="$media:icon"/>

    <Text
        ohos:id="$+id:msg_toast"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="vertical_center"
        ohos:text_size="12fp"/>

</DirectionalLayout>

background_toast_element.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <solid
        ohos:color="#6B172F6D"/>
    <corners
    	ohos:radius="10vp"/>
</shape>

ToastUtil:

先加载layout_toast布局文件把渲染的消息放进去然后让new出来的ToastDialog加载布局即可。

public class ToastUtil {

    /**
     * @param context 上下文参数
     * @param msg     内容
     */
    public static void showToast(Context context, String msg) {
        DirectionalLayout layout = (DirectionalLayout) LayoutScatter.getInstance(context)
                .parse(ResourceTable.Layout_layout_toast, null, false);
        Text msg_toast = layout.findComponentById(ResourceTable.Id_msg_toast);
        msg_toast.setText(msg);
        new ToastDialog(context)
                .setContentCustomComponent(layout)
                .setSize(DirectionalLayout.LayoutConfig.MATCH_CONTENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT)
                .setAlignment(LayoutAlignment.TOP)
                .show();
    }

    public static void showTips(Context context, String msg) {
        new ToastDialog(context).setText(msg).setAlignment(LayoutAlignment.TOP).show();
    }
}

5.Github源码

源码地址:WeatherDemo

视star情况更新。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-12-20 12:11:46修改
2
收藏
回复
举报
回复
    相关推荐