鸿蒙OS开发:【一次开发,多端部署】(一多天气)项目

一多天气

介绍

本示例展示一个天气应用界面,包括首页、城市管理、添加城市、更新时间弹窗,体现一次开发,多端部署的能力。

1.本示例参考一次开发,多端部署的指导,主要使用响应式布局的栅格断点系统实现在不同尺寸窗口界面上不同的显示效果。

2.使用[SideBarContainer]实现侧边栏功能。

3.使用[栅格容器组件]实现界面内容的分割和展示。

4.使用Canvas和CanvasRenderingContext2D完成空气质量和日出月落图的曲线绘制。

开发前请熟悉鸿蒙开发指导文档gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。

效果预览

image.png

使用说明:

1.启动应用后,首页展示已添加城市的天气信息,默认展示2个城市,左右滑动可以切换城市,在LG设备上,默认显示侧边栏,侧边栏显示时,右侧内容区占2/3,侧边栏隐藏时,内容区自动铺满界面。

2.在支持窗口自由拖拽的设备上,拖拽窗口大小,可以分别实现拖动到最大窗口侧边栏显示(点击侧边栏控制按钮可以隐藏和显示侧边栏),拖动窗口缩小到MD大小时侧边栏和侧边栏控制按钮隐藏。

3.在支持窗口自由拖拽的设备上,拖拽窗口大小,天气内容区跟随窗口大小会自动换行显示。

4.点击右上角菜单按钮,在菜单中点击更新时间,弹出更新时间弹窗,没有功能,此处只做展示,在平板设备上显示2列,在小屏设备上显示一列。

5.点击右上角菜单按钮,在菜单中点击管理城市,进入管理城市界面,展示已添加的城市,在平板设备上显示2列,在小屏设备上显示一列。

6.点击管理城市界面的添加城市,进入添加城市界面,已添加的城市不可点击,未添加的城市点击可以添加并返回管理城市界面显示。

工程目录

/code/SuperFeature/MultiDeviceAppDev/Weather/product/default
└─src
    ├─main
    │  │
    │  ├─ets
    │  │  ├─Application
    │  │  │      MyAbilityStage.ts          //自定义ability
    │  │  │
    │  │  ├─common                          //公共资源库
    │  │  ├─feature
    │  │  │      AirQualityFeature.ts       //空气绘画
    │  │  │      SunCanvasFeature.ts        //晴天绘画
    │  │  │
    │  │  ├─MainAbility
    │  │  │      MainAbility.ts             //主窗口
    │  │  │
    │  │  └─pages
    │  │      │  AddCity.ets                //添加城市
    │  │      │  CityList.ets               //城市列表
    │  │      │  Home.ets                   //入口
    │  │      │
    │  │      └─home
    │  │              AirQuality.ets         //空气质量
    │  │              HomeContent.ets        //主页面
    │  │              HoursWeather.ets       //每小时天气组件
    │  │              IndexEnd.ets           //首页尾 
    │  │              IndexHeader.ets        //首页头
    │  │              IndexTitleBar.ets      //首页标题
    │  │              LifeIndex.ets          //生活建议
    │  │              MultidayWeather.ets    //天气组件
    │  │              SideContent.ets        //侧边栏
    │  │              SunCanvas.ets          //晴天样式
    │  │              UpdateTimeDialog.ets   //时间更新弹窗
    │  │
    │  └─resources                           //资源包                                             

具体实现

1、home.ets中引入SideContent()和homeContent()。
2、定义showSideBar来判断是否展示侧边栏,定义mediaquery.MediaQueryListener媒体监听器smListener、mdListener、lgListener。
3、在aboutToAppear调用mediaquery对界面进行监听,[源码参考]。

/*

 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



import mediaquery from '@ohos.mediaquery';

import HomeContent from './home/HomeContent';

import IndexTitleBar from './home/IndexTitleBar';

import SideContent from './home/SideContent';

import { CityListData, Style, getBg, getCityListWeatherData, Logger } from '@ohos/common';



const TAG: string = 'Home';



@Entry

@Component

struct Home {

  @StorageLink('isRefresh') @Watch('refreshChange') isRefresh: boolean = false;

  @StorageLink('swiperIndex') swiperIndex: number = 0;

  @State curBp: string = 'md';

  @State cityListWeatherData: CityListData[] = getCityListWeatherData();

  @State popupState: boolean = false;

  @State showSideBar: boolean = false;

  private smListener: mediaquery.MediaQueryListener;

  private mdListener: mediaquery.MediaQueryListener;

  private lgListener: mediaquery.MediaQueryListener;



  build() {

    SideBarContainer(SideBarContainerType.Embed) {

      SideContent({ showSideBar: $showSideBar })

        .height('100%')

      Column() {

        IndexTitleBar({ showSideBar: $showSideBar })

          .height(56)

        Swiper() {

          ForEach(this.cityListWeatherData, (item, index) => {

            HomeContent({ showSideBar: this.showSideBar, cityListData: item, index: index })

          }, item => item.city)

        }

        .id('swiper')

        .padding({ left: Style.NORMAL_PADDING, right: Style.NORMAL_PADDING })

        .indicatorStyle({

          selectedColor: Color.White

        })

        .onChange(index => {

          this.swiperIndex = index;

          AppStorage.SetOrCreate('swiperIndex', this.swiperIndex);

        })

        .indicator(this.curBp !== 'lg')

        .index(this.swiperIndex)

        .loop(false)

        .width('100%')

        .layoutWeight(1)

      }

      .height('100%')

    }

    .height('100%')

    .sideBarWidth('33.3%')

    .minSideBarWidth('33.3%')

    .maxSideBarWidth('33.3%')

    .showControlButton(false)

    .showSideBar(this.showSideBar)

    .backgroundImageSize(ImageSize.Cover)

    .backgroundImage(getBg(this.cityListWeatherData[this.swiperIndex].header.weatherType))

  }



  aboutToAppear() {

    this.smListener = mediaquery.matchMediaSync('(320vp<width<=600vp)');

    this.smListener.on("change", this.isBreakpointSM);

    this.mdListener = mediaquery.matchMediaSync('(600vp<width<=840vp)');

    this.mdListener.on("change", this.isBreakpointMD);

    this.lgListener = mediaquery.matchMediaSync('(840vp<width)');

    this.lgListener.on("change", this.isBreakpointLG);

  }



  aboutToDisappear() {

    this.smListener.off("change", this.isBreakpointSM);

    this.mdListener.off("change", this.isBreakpointMD);

    this.lgListener.off("change", this.isBreakpointLG);

  }



  isBreakpointSM = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      this.curBp = 'sm';

      this.showSideBar = false;

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }

  isBreakpointMD = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      this.curBp = 'md';

      this.showSideBar = false;

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }

  isBreakpointLG = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      if (this.curBp !== 'lg') {

        this.showSideBar = true;

      }

      this.curBp = 'lg';

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }



  refreshChange() {

    Logger.info(TAG, `refreshChange}`);

    if (this.isRefresh) {

      this.cityListWeatherData = getCityListWeatherData();

      AppStorage.SetOrCreate('isRefresh', false);

    }

    Logger.info(TAG, `refreshChange, this.cityListWeatherData.length = ${this.cityListWeatherData.length}`);

  }

}

`HarmonyOS与OpenHarmony鸿蒙文档籽料:mau123789是v直接拿`

搜狗高速浏览器截图20240326151450.png

4、监听到当前屏幕大小,调用this.isBreakpoint断点,对curBp、showSideBar进行赋值,[源码参考]。

/*

 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



import mediaquery from '@ohos.mediaquery';

import HomeContent from './home/HomeContent';

import IndexTitleBar from './home/IndexTitleBar';

import SideContent from './home/SideContent';

import { CityListData, Style, getBg, getCityListWeatherData, Logger } from '@ohos/common';



const TAG: string = 'Home';



@Entry

@Component

struct Home {

  @StorageLink('isRefresh') @Watch('refreshChange') isRefresh: boolean = false;

  @StorageLink('swiperIndex') swiperIndex: number = 0;

  @State curBp: string = 'md';

  @State cityListWeatherData: CityListData[] = getCityListWeatherData();

  @State popupState: boolean = false;

  @State showSideBar: boolean = false;

  private smListener: mediaquery.MediaQueryListener;

  private mdListener: mediaquery.MediaQueryListener;

  private lgListener: mediaquery.MediaQueryListener;



  build() {

    SideBarContainer(SideBarContainerType.Embed) {

      SideContent({ showSideBar: $showSideBar })

        .height('100%')

      Column() {

        IndexTitleBar({ showSideBar: $showSideBar })

          .height(56)

        Swiper() {

          ForEach(this.cityListWeatherData, (item, index) => {

            HomeContent({ showSideBar: this.showSideBar, cityListData: item, index: index })

          }, item => item.city)

        }

        .id('swiper')

        .padding({ left: Style.NORMAL_PADDING, right: Style.NORMAL_PADDING })

        .indicatorStyle({

          selectedColor: Color.White

        })

        .onChange(index => {

          this.swiperIndex = index;

          AppStorage.SetOrCreate('swiperIndex', this.swiperIndex);

        })

        .indicator(this.curBp !== 'lg')

        .index(this.swiperIndex)

        .loop(false)

        .width('100%')

        .layoutWeight(1)

      }

      .height('100%')

    }

    .height('100%')

    .sideBarWidth('33.3%')

    .minSideBarWidth('33.3%')

    .maxSideBarWidth('33.3%')

    .showControlButton(false)

    .showSideBar(this.showSideBar)

    .backgroundImageSize(ImageSize.Cover)

    .backgroundImage(getBg(this.cityListWeatherData[this.swiperIndex].header.weatherType))

  }



  aboutToAppear() {

    this.smListener = mediaquery.matchMediaSync('(320vp<width<=600vp)');

    this.smListener.on("change", this.isBreakpointSM);

    this.mdListener = mediaquery.matchMediaSync('(600vp<width<=840vp)');

    this.mdListener.on("change", this.isBreakpointMD);

    this.lgListener = mediaquery.matchMediaSync('(840vp<width)');

    this.lgListener.on("change", this.isBreakpointLG);

  }



  aboutToDisappear() {

    this.smListener.off("change", this.isBreakpointSM);

    this.mdListener.off("change", this.isBreakpointMD);

    this.lgListener.off("change", this.isBreakpointLG);

  }



  isBreakpointSM = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      this.curBp = 'sm';

      this.showSideBar = false;

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }

  isBreakpointMD = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      this.curBp = 'md';

      this.showSideBar = false;

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }

  isBreakpointLG = (mediaQueryResult) => {

    if (mediaQueryResult.matches) {

      if (this.curBp !== 'lg') {

        this.showSideBar = true;

      }

      this.curBp = 'lg';

      AppStorage.SetOrCreate('curBp', this.curBp);

    }

    Logger.info(TAG, `this.curBp = ${this.curBp}`);

  }



  refreshChange() {

    Logger.info(TAG, `refreshChange}`);

    if (this.isRefresh) {

      this.cityListWeatherData = getCityListWeatherData();

      AppStorage.SetOrCreate('isRefresh', false);

    }

    Logger.info(TAG, `refreshChange, this.cityListWeatherData.length = ${this.cityListWeatherData.length}`);

  }

}
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值