【HarmonyOS应用开】实现天气预报 您所在的位置:网站首页 华为手机屏幕上有两个天气预报 【HarmonyOS应用开】实现天气预报

【HarmonyOS应用开】实现天气预报

2024-07-05 14:16| 来源: 网络整理| 查看: 265

作者:韩茹

公司:程序咖(北京)科技有限公司

鸿蒙巴士专栏作家

本案例用到了PageSlider,PageSliderIndicator,PageSliderProvider,service服务,json解析,网络下载等等。。

一、项目展示

首先我们先新建一个HarmonyOS的项目:

WX20210707-174310@2x

运行效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rf1IK2R-1630899683472)(https://img.chengxuka.com/tianqiyubaoyunxing1.gif)]

二、布局文件

现在ability_main.xml中添加:

graphic下,button_bg.xml:

预览效果:

WX20210707-175153@2x

三、Service服务

1、先获取布局中的组件

在MainAbilitySlice.java中,添加initComponent()方法先获取组件,并在onStart()中进行调用:

// 初始化组件 private void initComponent() { textCity = (Text) findComponentById(ResourceTable.Id_show_city); textDate = (Text) findComponentById(ResourceTable.Id_show_date); textTemp = (Text) findComponentById(ResourceTable.Id_show_temp); textHumidity = (Text) findComponentById(ResourceTable.Id_show_humidity); textSpeed = (Text) findComponentById(ResourceTable.Id_show_wind); inputCityTextField = (TextField) findComponentById(ResourceTable.Id_input_city); Button btnQuery = (Button) findComponentById(ResourceTable.Id_btn_query); } @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); initComponent(); }

2、创建Service服务

WX20210707-180518@2x

在生成的ServiceAbility中,先修改一下HiLogLabel:

private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0x00201, "WeatherServiceAbility");

在MainAbilitySlice中,也添加一下HiLogLabel:

private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0x00201, "MainAbilitySlice");

3、点击 按钮,连接 service

在MainAbilitySlice中,先 添加两个 成员变量:

private static final String BUNDLE_NAME = "com.example.hanruweather"; private static final String SERVICE_NAME = "WeatherServiceAbility";

添加一个方法,用于获取链接Service的Intent:

// 获取启动本地服务的Intent private Intent getServiceIntent(String bundleName, String serviceName) { Operation operation = new Intent.OperationBuilder().withDeviceId("") .withBundleName(bundleName) .withAbilityName(serviceName) .build(); Intent intent = new Intent(); intent.setOperation(operation); return intent; }

在onStart()中,连接服务

@Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); initComponent(); HiLog.info(LABEL_LOG, "connectService------ "); // 连接服务 connectAbility(getServiceIntent(BUNDLE_NAME, SERVICE_NAME), connection); }

接下来创建IAbilityConnection类型的连接对象:

// 连接对象 private IAbilityConnection connection = new IAbilityConnection() { @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { clientRemoteProxy = new ClientRemoteProxy(iRemoteObject); } @Override public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { HiLog.info(LABEL_LOG, "%{public}s", "onAbilityDisconnectDone resultCode : " + resultCode); } };

这里需要一个IRemoteBroker实现类:

// 客户端代理类 public class ClientRemoteProxy implements IRemoteBroker { private static final int RESULT_SUCCESS = 0; private final IRemoteObject remoteObject; public ClientRemoteProxy(IRemoteObject remoteObject) { this.remoteObject = remoteObject; } // 这里是消息发送最关键的地方 public void todoServiceJob(int command) { } @Override public IRemoteObject asObject() { return remoteObject; } }

我们的网络请求分两种,一种是当天的天气,一种是预测未来4天的。

我们定义两个常量:

先定义个包constant,里面定义个常量类Constant.java:

package com.example.hanruweather.constant; /** * 常量 */ public class Constant { public static final int CURRENT = 1001; public static final int FORECAST = 1002; public static final int SUCCESS = 1; public static final int FAIL = 0 ; }

在initComponent()方法中,点击按钮,调用todoServiceJob()方法,进行请求服务:

// 初始化组件 private void initComponent() { ... btnQuery.setClickedListener(component -> { clientRemoteProxy.todoServiceJob(Constant.CURRENT); }); }

接下来,我们就要在todoServiceJob()中,先封装数据,获取输入框中用户要查询天气的城市,然后通过sendRequest()进行请求:

public void todoServiceJob(int command) { // 传过去的参数:城市名称 MessageParcel message = MessageParcel.obtain(); String city = inputCityTextField.getText(); HiLog.info(LABEL_LOG, "textfield-->city------ " + city); message.writeString(city); // 接收回来的参数 MessageParcel reply = MessageParcel.obtain(); try { // 发送请求 HiLog.info(LABEL_LOG, "sendRequest------ "); remoteObject.sendRequest(command, message, reply, option); } catch (RemoteException e) { e.printStackTrace(); } }

然后在WeatherServiceAbility中,先创建RemoteObject子类并实现IRemoteBroker:

class MyRemote extends RemoteObject implements IRemoteBroker { MyRemote() { super("MyService_MyRemote"); HiLog.info(LABEL_LOG, "MyRemote()----->"); } // 设置接收请求的条目。 // int code:表示从对等端发送的服务请求代码。 @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { return true; } @Override public IRemoteObject asObject() { return this; } }

声明一个成员变量:

public class WeatherServiceAbility extends Ability { ... private MyRemote remote = new MyRemote(); ... }

在onConnect()方法中返回:

@Override protected IRemoteObject onConnect(Intent intent) { super.onConnect(intent); HiLog.info(LABEL_LOG, "onConnect()---->"); return remote.asObject(); }

到此我们先运行一下,输入城市并点击查询按钮:

WX20210707-183952@2x

四、网络请求

1、这里我们使用https://openweathermap.org/api中的json接口,这个接口中提供的天气预报 的数据是全球的,也就是说我们的程序写完,你也可以查询国外城市的天气预报。

WX20210708-092750@2x

我们先去Constant常量类中定义json数据的网址:

// 天气预报的base地址 public static final String BASE_URI = "https://api.openweathermap.org/data/2.5/"; // private static final String API_KEY ="appid={{Add your Key}}"; public static final String API_KEY = "appid=accf59680d5b2039f92c468d4ac8e634"; // 预测4天 public static final String FORECAST_URL = "forecast"; // 当天天气 public static final String WEATHER_URL = "weather";

2、我们在onRemoteRequest()中获取MainAbilitySlice中请求传来的城市数据

String cityData = data.readString();

但是因为用户可能传入的是中文,如果是中国的城市名称,我们需要转为对应的汉语拼音。这里我们要借助于一个第三方jar包:

WX20210708-093514@2x

将这个jar包拷贝到libs目录下,并右键 Add as Library。

然后我们创建一个package,utils,里面专门存放一些工具类,先来一个TextUtils类:

package com.example.hanruweather.utils; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; public class TextUtils { /** * 将汉字转为汉语拼音 * * @param text * @return */ public static String convertToHanYuPinYin(String text) { // 如果不是中文,直接 返回原字符串 if (!text.matches("[\\u4E00-\\u9FA5]+")) { return text; } // 如果是中文,转为汉语拼音 char[] array = text.toCharArray(); StringBuffer sb = new StringBuffer(); HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); // 不显示拼音的声调 format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (int i = 0; i sb.append(PinyinHelper.toHanyuPinyinStringArray(array[i], format)[0]); } catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) { badHanyuPinyinOutputFormatCombination.printStackTrace(); } } return sb.toString(); } }

在onRemoteRequest()中:

HiLog.info(LABEL_LOG, "onRemoteRequest()----->"); String cityData = data.readString(); city = TextUtils.convertToHanYuPinYin(cityData); HiLog.info(LABEL_LOG, "onRemoteRequest---->" + code + ",--->cityData:" + cityData + "---city:" + city);

这里我们再提供一个方法,用来拼接URL

// 拼接URL private String concatUrl(String link, String params[]) { StringBuilder urlFinal = new StringBuilder(); urlFinal.append(link); urlFinal.append("?"); for (int i = 0; i /** * 根据指定的url,下载天气预报的数据:json格式 * @param weatherUrl * @return */ public static String downLoadWeatherData(String weatherUrl){ NetManager netManager = NetManager.getInstance(null); if (!netManager.hasDefaultNet()) { return null; } NetHandle netHandle = netManager.getDefaultNet(); // Listen to network state changes. NetStatusCallback callback = new NetStatusCallback() { // Override the callback for network state changes. }; netManager.addDefaultNetStatusCallback(callback); // Obtain a URLConnection using the openConnection method. HttpURLConnection connection = null; try { URL url = new URL(weatherUrl); URLConnection urlConnection = netHandle.openConnection(url, java.net.Proxy.NO_PROXY); if (urlConnection instanceof HttpURLConnection) { connection = (HttpURLConnection) urlConnection; } connection.setRequestMethod("GET"); connection.connect(); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line); } br.close(); return sb.toString(); } return null; } catch (IOException e) { e.printStackTrace(); return "IOException"; } finally { if (connection != null) { connection.disconnect(); } } } }

记得在config.json中添加网络权限:

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

到此,我们启动程序,并在输入框中输入:北京

WX20210708-095327@2x

能够将json数据下载到了。

五、json解析

我们首先观察json接口中的字段的描述:https://openweathermap.org/current#current_JSON

WX20210708-095517@2x

根据我们想要的字段,进行json解析,解析的方式有很多种,也有很多第三方的包,这里我使用的是JSON

WX20210708-095651@2x

可以将json字符串解析对应JavaBean的类,也可以解析到集合中,比如Map等。

然后我们在utils包下,新建一个json解析的工具类:

package com.example.hanruweather.utils; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; import java.util.Map; public class JsonUtils { /** * 解析当前的天气预报-->Map集合 * @param json * @return */ public static Map parseCurrentWeatherJsonToMap(String json){ Map map = new HashMap(); if(json==null|| "".equals(json)){ return map; } try { JSONObject jSONObject = new JSONObject(json); // 温度 JSONObject main = jSONObject.getJSONObject("main"); // 将开尔文转为摄氏度:开氏度 = 摄氏度+273.15。 map.put("temp",String.format("%.2f",main.optDouble("temp")-273.15)); // 温度 map.put("humidity",main.optString("humidity")); // 湿度 JSONObject wind = jSONObject.getJSONObject("wind"); map.put("speed",wind.optString("speed")); // 风速 JSONObject sys = jSONObject.getJSONObject("sys"); map.put("country", sys.optString("country")); } catch (JSONException e) { e.printStackTrace(); } return map; } }

这里我将它解析到Map集合中了。

六、数据处理

解析后的数据,我们希望能够将它显示到UI界面上。在onRemoteRequest()中,需要将数据写出去。

// 设置接收请求的条目。 // int code:表示从对等端发送的服务请求代码。 @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { HiLog.info(LABEL_LOG, "onRemoteRequest()----->"); String cityData = data.readString(); city = TextUtils.convertToHanYuPinYin(cityData); HiLog.info(LABEL_LOG, "onRemoteRequest---->" + code + ",--->cityData:" + cityData + "---city:" + city); switch (code) { case Constant.CURRENT: // 下载当前的天气 String weatherUrl = concatUrl(Constant.BASE_URI + Constant.WEATHER_URL, new String[]{"q=" + city, Constant.API_KEY}); String jsonData = NetDownLoadUtils.downLoadWeatherData(weatherUrl); HiLog.info(LABEL_LOG, "json-->" + jsonData); Map map = JsonUtils.parseCurrentWeatherJsonToMap(jsonData); if (map.size() == 0) { reply.writeInt(Constant.FAIL); return false; } map.put("city", cityData); reply.writeInt(Constant.SUCCESS); reply.writeString(ZSONObject.toZSONString(map)); HiLog.info(LABEL_LOG, "map-->" + map); break; default: } return true; }

在MainAbilitySlice中,todoServiceJob()的完整代码:

// 这里是消息发送最关键的地方 public void todoServiceJob(int command) { // 传过去的参数:城市名称 MessageParcel message = MessageParcel.obtain(); String city = inputCityTextField.getText(); HiLog.info(LABEL_LOG, "textfield-->city------ " + city); message.writeString(city); // 接收回来的参数 MessageParcel reply = MessageParcel.obtain(); // 异步还是同步 MessageOption option = new MessageOption(MessageOption.TF_SYNC); try { // 发送请求 HiLog.info(LABEL_LOG, "sendRequest------ "); remoteObject.sendRequest(command, message, reply, option); // 处理结果 int result = reply.readInt(); if (result == Constant.FAIL) { new ToastDialog(getContext()).setText("查无数据").setAlignment(LayoutAlignment.CENTER).show(); } else if (result == Constant.SUCCESS) { switch (command) { case Constant.CURRENT: String jsonData = reply.readString(); HiLog.info(LABEL_LOG, "jsonData------ " + jsonData); Map map = ZSONObject.stringToClass(jsonData, Map.class); HiLog.info(LABEL_LOG, "map------ " + map); // 将接收到到数据,设置到UI组件上 textCity.setText(map.get("city") + "," + map.get("country")); String date = DateUtils.getCurrentDate(); textDate.setText(date + ", " + DateUtils.getWeekOfCurrentDay()); textTemp.setText(map.get("temp") + "°C"); textHumidity.setText("空气湿度:" + map.get("humidity") + "%"); textSpeed.setText("风速:" + map.get("speed") + "km/hr"); break; default: } } } catch (RemoteException e) { e.printStackTrace(); } } @Override public IRemoteObject asObject() { return remoteObject; } }

因为我们打算显示日期的时候,显示星期几,所以在utils包下,再提供一个日期的工具类:

package com.example.hanruweather.utils; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class DateUtils { public static String getCurrentDate(){ Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String dateStr = sdf.format(date); return dateStr; } /** * 获取今天星期几 * @return */ public static String getWeekOfCurrentDay(){ String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; Calendar cal = Calendar.getInstance(); int week = cal.get(Calendar.DAY_OF_WEEK); return weekDays[week-1]; } }

到此,我们运行一下程序:

tianqiyubaoyunxing2

七、获取更多天气

想获预测未来4天的天气,UI上可以借助于PageSlider,用来进行切换。获取未来4天的天气的json数据和上面获取当前天气的思路原理是一样的,只是更换json接口而已。然后进行相应的解析。再将数据通过PageSliderProvider,显示到PageSlider上。完整的代码,我放在github上了。

下载源代码

更多内容:

1、社区:鸿蒙巴士https://www.harmonybus.net/

2、公众号:HarmonyBus

3、技术交流QQ群:714518656

4、视频课:https://www.chengxuka.com



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有