项目概述
weather是一款基于Qt/QML开发的HarmonyOS天气应用,旨在提供轻量、稳定、可扩展的天气信息服务。该应用实现了城市搜索/收藏、当前天气展示、小时预报等核心功能,同时具备适配弱网环境、跨分辨率自适应等特性。

项目地址:https://gitcode.com/szkygc/weather
技术栈概览
- 平台: HarmonyOS (鸿蒙)
- 前端框架: Qt/QML
- 后端语言: C++
- 数据获取: QNetworkAccessManager
- 数据解析: QJsonDocument
- UI组件: QtQuick.Controls, Layouts, GraphicalEffects
- 数据源: APIHZ聚合接口、OpenWeather兼容结构
核心功能特性
- 🏙️ 城市搜索:支持城市搜索和快速查询
- 🌡️ 当前天气:展示温度、天气现象、体感温度、湿度、风向风速等详细信息
- 🎨 自适应UI:支持不同分辨率设备,全局缩放适配
- 🔄 容错机制:多节点自动重试、数据格式兼容、图标失败兜底
- 📱 优质体验:渐变背景、半透明卡片、阴影圆角等视觉效果
架构设计
项目结构
weather/
├── entry/src/main/cpp/
│ ├── main.qml # 应用主界面
│ ├── main.cpp # 应用入口
│ ├── weatherdatafetcher.cpp/.h # 数据获取与解析
│ └── CMakeLists.txt # 构建配置
└── docs/ # 项目文档
模块划分
- 界面层 :
main.qml负责所有UI展示和用户交互 - 数据层 :
WeatherDataFetcher处理网络请求、数据解析和错误处理
核心技术实现
1. 数据获取与处理
cpp
// 天气数据获取类的关键属性
explicit WeatherDataFetcher(QObject *parent = nullptr);
Q_PROPERTY(QString cityName READ cityName NOTIFY cityNameChanged)
Q_PROPERTY(QString temperature READ temperature NOTIFY temperatureChanged)
Q_PROPERTY(QString weatherCondition READ weatherCondition NOTIFY weatherConditionChanged)
Q_PROPERTY(QString humidity READ humidity NOTIFY humidityChanged)
Q_PROPERTY(QString windSpeed READ windSpeed NOTIFY windSpeedChanged)
Q_PROPERTY(QString pressure READ pressure NOTIFY pressureChanged)
Q_PROPERTY(QString visibility READ visibility NOTIFY visibilityChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(QString apiHzId READ apiHzId WRITE setApiHzId NOTIFY apiHzIdChanged)
Q_PROPERTY(QString apiHzKey READ apiHzKey WRITE setApiHzKey NOTIFY apiHzKeyChanged)
Q_PROPERTY(QVariantList hourly READ hourly NOTIFY hourlyChanged)
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
Q_PROPERTY(QString feelsLike READ feelsLike NOTIFY feelsLikeChanged)
Q_PROPERTY(QString windDirection READ windDirection NOTIFY windDirectionChanged)
Q_PROPERTY(QString windScale READ windScale NOTIFY windScaleChanged)
Q_PROPERTY(QString precipitation READ precipitation NOTIFY precipitationChanged)
Q_PROPERTY(QString updateTime READ updateTime NOTIFY updateTimeChanged)
// 关键方法和信号
Q_INVOKABLE void fetchWeather(const QString &city);
Q_INVOKABLE void fetchWeatherByLocation(double latitude, double longitude);
Q_INVOKABLE void setApiHzId(const QString &id);
Q_INVOKABLE void setApiHzKey(const QString &key);
signals:
void weatherFetched(bool success, const QString &error = "");
void hourlyChanged();
2. 网络请求与容错机制
cpp
// 多节点候选与自动重试
void WeatherDataFetcher::issueApiHzRequest()
{
if (m_apiHzBases.isEmpty()) {
prepareApiHzCandidates();
}
const int idx = qBound(0, m_apiHzTryIndex, m_apiHzBases.size() - 1);
const QString base = m_apiHzBases.at(idx);
const QString url = buildApiHzUrl(base, m_lastShengParam, m_lastPlaceParam);
QNetworkRequest req;
req.setUrl(QUrl(url));
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
req.setAttribute(QNetworkRequest::User, QVariant("apihz"));
m_networkManager->get(req);
}
3. 图标资源规范化处理
cpp
static inline QString normalizeIconUrl(const QString &raw)
{
if (raw.isEmpty()) return QString();
if (raw.startsWith(QStringLiteral("https://"))) {
const QUrl u(raw);
const QString host = u.host();
QString fileName = QFileInfo(u.path()).fileName();
if (!fileName.isEmpty() && host.contains(QStringLiteral("rescdn.apihz.cn"))) {
if (fileName.endsWith(QStringLiteral(".png"), Qt::CaseInsensitive)) {
fileName.chop(4);
fileName += QStringLiteral(".gif");
}
return QStringLiteral("http://cn.apihz.cn/skin/tianqi/icon/") + fileName;
}
return raw;
}
if (raw.startsWith(QStringLiteral("http://"))) return raw;
return QStringLiteral("http://cn.apihz.cn/skin/tianqi/icon/") + raw;
}
4. QML界面实现
城市标题与搜索/收藏功能
qml
// 城市信息 + 搜索/收藏按钮
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 20 * root.uiScale
spacing: 10 * root.uiScale
Text { text: weatherFetcher.cityName || "北京"; font.pixelSize: 36 * root.uiScale; font.bold: true; color: "white" }
Button {
text: "🔍"
background: Rectangle { radius: 20 * root.uiScale; color: "#4DFFFFFF" }
onClicked: searchDialog.open()
}
// 收藏按钮已移除
}
自适应详情网格布局
qml
GridLayout {
id: gridCards
Layout.fillWidth: true
columnSpacing: 12 * root.uiScale
rowSpacing: 12 * root.uiScale
readonly property int targetCols: 3
readonly property real cardMinWidth: 160 * root.uiScale
readonly property int computedCols: Math.max(1, Math.min(targetCols, Math.floor(width / (cardMinWidth + columnSpacing))))
columns: computedCols
}
// 小时预报组件已移除
项目配置与构建
主要配置文件
- CMakeLists.txt:定义Qt依赖、构建参数和输出路径
- module.json5:配置应用入口和能力
- main.cpp:初始化Qt应用并加载QML
开发与运行步骤
- 环境配置:安装Qt for HarmonyOS的SDK和工具链
- API配置 :在
main.qml中设置apiHzId和apiHzKey - 构建运行:编译并部署到HarmonyOS设备
- 调试监控 :关注
weatherFetched信号和控制台日志
关键问题解决方案
1. HTTPS兼容性问题
- 问题:Qt for HarmonyOS环境下HTTPS协议支持受限
- 解决方案 :使用HTTP协议替代,通过
normalizeIconUrl函数处理URL转换
2. 界面自适应
- 问题:不同设备分辨率下界面布局需要适配
- 解决方案 :实现全局
uiScale缩放因子,基于短边800为基准进行计算
3. 网络容错
- 问题:弱网环境下API请求可能失败
- 解决方案:实现多节点候选列表,失败自动切换重试
4. 数据格式兼容
- 问题:API返回格式可能变化或不一致
- 解决方案:优先解析APIHZ结构,失败时尝试解析OpenWeather结构
性能优化
- 图标缓存 :
Image组件启用cache: true提高加载效率 - 异步加载 :使用
asynchronous: true避免阻塞UI线程 - 网格自适应:动态计算列数以适应不同屏幕宽度
- 内存管理:合理处理网络请求和JSON解析,避免内存泄漏
调试技巧
- 信号监控 :监听
weatherFetched信号和loading状态 - 日志分析:观察原始JSON数据和解析过程
- UI调试 :通过调整
uiScale因子快速验证不同尺寸显示效果 - 错误模拟:可以通过修改API凭证或网络设置来测试错误处理机制
项目效果展示
- ✅ 城市搜索功能正常工作
- ✅ 当前天气信息完整展示
- ✅ 界面自适应不同分辨率设备
- ✅ 在弱网环境下具备容错能力
- ✅ 图标加载失败时有兜底处理
学习要点
- Qt与HarmonyOS集成:掌握Qt框架在HarmonyOS平台的部署和使用
- C++与QML交互:通过属性、信号槽机制实现数据绑定
- 跨平台适配:实现界面自适应和网络兼容
- 容错设计:构建健壮的网络请求和数据处理流程
- 简化架构:移除不必要组件,保持代码简洁高效
未来扩展方向
- 功能增强:增加15天预报、AQI空气质量、生活指数等功能
- 用户体验:引入定位服务、多语言支持、主题切换
- 技术优化:实现数据缓存、离线模式、更丰富的天气动效
- 架构升级:模块化服务抽象,支持更多数据源
总结
weather项目成功验证了Qt/QML在HarmonyOS平台上的可行性和实用性。通过多节点重试、格式容错、资源规范化等策略,应用能够在复杂网络环境下提供稳定的用户体验。项目架构清晰,代码组织合理,具备良好的可扩展性,可以作为学习和二次开发的优秀模板。
日期: 2025年11月