05-自然壁纸实战教程-首页 原创

万少skr
发布于 2025-7-8 14:49
浏览
0收藏

05-自然壁纸实战教程-首页

前言

这一章节需要完成的功能主要有沉浸式、首页搜索框、轮播大图、分类、精选主题、壁纸列表还有 tab容器结构

05-自然壁纸实战教程-首页-鸿蒙开发者社区

tab容器结构

05-自然壁纸实战教程-首页-鸿蒙开发者社区

元服务的tab结构需要使用 AtomicServiceNavigation来实现

基本示例

import { AtomicServiceNavigation, MixMode, GradientAlpha, BackgroundTheme } from '@kit.ArkUI';
import { AtomicServiceTabs, TabBarOptions, TabBarPosition } from '@kit.ArkUI';
@Entry
@Component
struct Index {
  @State message: string = '主题';
  childNavStack: NavPathStack = new NavPathStack();
  @Builder
  tabContent1() {
    Text('first page')
      .onClick(() => {
        this.childNavStack.pushPath({ name: 'page one' })
      })
  }


  @Builder
  tabContent2() {
    Text('second page')
  }


  @Builder
  tabContent3() {
    Text('third page')
  }


  @Builder
  navigationContent() {
    AtomicServiceTabs({
      tabContents: [
        () => {
          this.tabContent1()
        },
        () => {
          this.tabContent2()
        },
        () => {
          this.tabContent3()
        }
      ],
      tabBarOptionsArray: [
        new TabBarOptions($r('sys.media.ohos_ic_public_phone'), '功能1'),
        new TabBarOptions($r('sys.media.ohos_ic_public_location'), '功能2', Color.Green, Color.Red),
        new TabBarOptions($r('sys.media.ohos_ic_public_more'), '功能3')
      ],
      tabBarPosition: TabBarPosition.BOTTOM,
      barBackgroundColor: $r('sys.color.ohos_id_color_bottom_tab_bg'),
      onTabBarClick: (index: Number) => {
        if (index == 0) {
          this.message = '功能1';
        } else if (index == 1) {
          this.message = '功能2';
        } else {
          this.message = '功能3';
        }
      }
    })
  }


  @Builder
  pageMap(name: string) {
    if (name === 'page one') {
      PageOne()
    } else if (name === 'page two') {
      PageTwo()
    }
  }


  build() {
    Row() {
      Column() {
        AtomicServiceNavigation({
          navigationContent: () => {
            this.navigationContent()
          },
          title: this.message,
          titleOptions: {
            isBlurEnabled: false
          },
          gradientBackground: {
            primaryColor: '#FF0000',
            secondaryColor: '#00FF00',
            backgroundTheme: BackgroundTheme.LIGHT,
            mixMode: MixMode.AVERAGE,
            alpha: GradientAlpha.OPACITY_100
          },
          navDestinationBuilder: this.pageMap,
          navPathStack: this.childNavStack,
          mode: NavigationMode.Stack
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}


@Component
export struct PageOne {
  pageInfo: NavPathStack = new NavPathStack();


  build() {
    NavDestination() {
      Button('Next')
        .onClick(() => {
          this.pageInfo.pushPath({ name: 'page two'})
        })
    }
    .title('PageOne')
    .onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
    })
  }
}


@Component
export struct PageTwo {
  pageInfo: NavPathStack = new NavPathStack();


  build() {
    NavDestination() {
      Button('End')
    }
    .title('PageTwo')
    .onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
    })
  }
}

效果

05-自然壁纸实战教程-首页-鸿蒙开发者社区

项目中的 AtomicServiceNavigation 使用

AtomicServiceNavigation({
  navigationContent: () => {
    this.navigationContent()
  },
  navPathStack: NavigationUtils.getInstance().pageInfos,// 封装的常量,指定跳转的页面
  hideTitleBar: true,
  mode: NavigationMode.Stack,
})

this.navigationContent()

  @Builder
  navigationContent() {
    AtomicServiceTabs({
      tabContents: [
        () => {
          this.tabContentHome()
        },
        () => {
          this.tabContentCategory()
        },
        () => {
          this.tabContentVideo()
        },
        () => {
          this.tabContentMine()
        }
      ],
      tabBarOptionsArray: [
        new TabBarOptions($r('app.media.index'), '首页', $r('app.color.common__bg'), "#53AA37"),
        new TabBarOptions($r('app.media.category'), '分类', $r('app.color.common__bg'), "#53AA37"),
        new TabBarOptions($r('app.media.video'), '视频', $r('app.color.common__bg'), "#53AA37"),
        new TabBarOptions($r('app.media.mine'), '我的', $r('app.color.common__bg'), "#53AA37"),
      ],
      tabBarPosition: TabBarPosition.BOTTOM,
      barBackgroundColor: $r('app.color.white'),
    })
      .padding({
        top: AppStatu.vpTopHeight,
        bottom: AppStatu.vpBottomHeight,
      })
  }

沉浸式

沉浸式的代码已经提前封装起来了 src/main/ets/utils/fullScreenHelper.ets

import { window } from "@kit.ArkUI";

/**
 * 安全区域
 */
@ObservedV2
export class AppStatu {
  @Trace static vpBottomHeight: number = 0
  @Trace static vpTopHeight: number = 0
}

export class FullScreenHelper {
  static window: window.Window

  static setWindow(window: window.Window) {
    FullScreenHelper.window = window
  }

  static setFullScreen() {
    FullScreenHelper.window.setWindowLayoutFullScreen(true)
    const topAvoidArea = FullScreenHelper.window.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
    const topRectHeight = topAvoidArea.topRect.height;
    const vpTopHeight = px2vp(topRectHeight)
    const bottomAvoidArea = FullScreenHelper.window.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
    const bottomRectHeight = bottomAvoidArea.bottomRect.height;
    const vpBottomHeight = px2vp(bottomRectHeight)
    AppStatu.vpBottomHeight = vpBottomHeight
    AppStatu.vpTopHeight = vpTopHeight

  }

  static setWindowSystemBarProperties(color: string) {
    window.getLastWindow(getContext())
      .then(win => {
        win.setWindowSystemBarProperties({
          statusBarContentColor: color, // 默认浅色文字(可自定义)
        });
      })
  }
}

然后在EntryAbility中使用

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.getMainWindow() // 沉浸式
      .then(win => {
        FullScreenHelper.setWindow(win)
        FullScreenHelper.setFullScreen()
      })
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

效果

05-自然壁纸实战教程-首页-鸿蒙开发者社区

首页

这里的首页不是指 src/main/ets/pages/Index.ets,而是指使用了AtomicServiceNavigation 管理的 首页 src/main/ets/views/homeView/HomeView.ets

05-自然壁纸实战教程-首页-鸿蒙开发者社区

首页搜索框

这里的图片和文字内容做成了可以轮训的样子

05-自然壁纸实战教程-首页-鸿蒙开发者社区

  /**
   * 顶部轮询语句框
   */
  @Builder
  PositiveQuotesBuilder() {
    Row() {
      Column() {
        Swiper() {
          ForEach(LocalData.PositiveQuotes, (quote: string) => {
            Row() {
              Image($r('app.media.app_icon'))
                .width(24)
                .height(24)
                .margin({ right: 10 })
              Text(quote)
                .fontSize(14)
                .fontColor('#666666')
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
            .width('100%')
            .height('100%')
            .backgroundColor($r('app.color.white'))
            .borderRadius(20)
            .padding({ left: 15, right: 15 })
            .justifyContent(FlexAlign.Start)
          })
        }
        .width('100%')
        .height(45)
        .autoPlay(true)
        .loop(true)
        .vertical(true)
        .interval(3000)
        .indicator(false)
      }
      .width('75%')
      .height(45)
      .borderRadius(20)
      .backgroundColor($r('app.color.white'))
      .shadow({
        radius: 4,
        color: 'rgba(0, 0, 0, 0.1)',
        offsetX: 0,
        offsetY: 2
      })
    }
    .width('100%')
    .justifyContent(FlexAlign.Start)
    .padding({ left: 15 })
    .margin({ top: 10, bottom: 15 })
  }

LocalData.PositiveQuotes 提供了基本的数据资源

  /**
   * 正能量语句数组
   */
  static readonly PositiveQuotes: string[] = [
    '每一天都是新的开始,充满无限可能',
    '坚持不懈,直到成功',
    '微笑面对生活,积极拥抱未来',
    '态度决定高度,行动创造价值',
    '今天的努力,是明天的收获',
    '相信自己,你比想象中更强大',
    '感恩所有,珍惜当下',
    '用心生活,用爱前行'
  ]

轮播大图

轮播图使用Swiper组件渲染即可

05-自然壁纸实战教程-首页-鸿蒙开发者社区

  // 精选壁纸轮播
  Swiper() {
    ForEach(LocalData.BannerData, (item: string) => {
      Image(item)
        .width('100%')
        .height(200)
        .borderRadius(10)
    })
  }
  .width('90%')
  .height(200)
  .autoPlay(true)
  .loop(true)
  .interval(3000)
  .borderRadius(10)
  //导航点颜色
  .indicator(
    Indicator.dot()
      .color('rgba(255, 255, 255, 0.3)')
      .selectedColor('#ff258075')
  )

LocalData.BannerData 负责提供轮播数据

  /**
   * 精选壁纸轮播数据
   */
  static readonly BannerData: string[] = [
    "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/1.png",
    "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/2.png",
    "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/3.png"
  ]

分类

05-自然壁纸实战教程-首页-鸿蒙开发者社区

可以滚动的结构,选择使用了 Scroll

  // 分类图标-- 横向列表
  Scroll() {
    Row() {
      ForEach(LocalData.CategoryData, (item: ICategory) => {
        CategoryItemView({ text: item.text, icon: item.icon, value: item.id })
          .margin({ right: 15 })
      })
    }
    .padding({ left: 15 })
  }
  .width("100%")
  .scrollable(ScrollDirection.Horizontal)
  .scrollBar(BarState.Off)

LocalData.CategoryData 对应的数据是

  /**
   * 类型数据
   */
  static readonly CategoryData: ICategory[] = [
    {
      "id": 0,
      "text": "背景",
      "value": "backgrounds",
      "icon": "🌅"
    },
    {
      "id": 1,
      "text": "时尚",
      "value": "fashion",
      "icon": "👔"
    },
    {
      "id": 2,
      "text": "自然",
      "value": "nature",
      "icon": "🌲"
    },
    {
      "id": 3,
      "text": "科学",
      "value": "science",
      "icon": "🔬"
    },
    {
      "id": 4,
      "text": "教育",
      "value": "education",
      "icon": "📚"
    },
    {
      "id": 5,
      "text": "感情",
      "value": "feelings",
      "icon": "❤️"
    },
    {
      "id": 6,
      "text": "健康",
      "value": "health",
      "icon": "🏥"
    },
    {
      "id": 7,
      "text": "人",
      "value": "people",
      "icon": "👥"
    },
    {
      "id": 8,
      "text": "宗教",
      "value": "religion",
      "icon": "🙏"
    },
    {
      "id": 9,
      "text": "地方",
      "value": "places",
      "icon": "🌆"
    },
    {
      "id": 10,
      "text": "动物",
      "value": "animals",
      "icon": "🐱"
    },
    {
      "id": 11,
      "text": "工业",
      "value": "industry",
      "icon": "🏭"
    },
    {
      "id": 12,
      "text": "计算机",
      "value": "computer",
      "icon": "💻"
    },
    {
      "id": 13,
      "text": "食品",
      "value": "food",
      "icon": "🍜"
    },
    {
      "id": 14,
      "text": "体育",
      "value": "sports",
      "icon": "🏃"
    },
    {
      "id": 15,
      "text": "交通",
      "value": "transportation",
      "icon": "🚗"
    },
    {
      "id": 16,
      "text": "旅行",
      "value": "travel",
      "icon": "✈️"
    },
    {
      "id": 17,
      "text": "建筑物",
      "value": "buildings",
      "icon": "🏢"
    },
    {
      "id": 18,
      "text": "商业",
      "value": "business",
      "icon": "💼"
    },
    {
      "id": 19,
      "text": "音乐",
      "value": "music",
      "icon": "🎵"
    }
  ]

精选主题

05-自然壁纸实战教程-首页-鸿蒙开发者社区

精选主题比较简单,就是标题和对应的内容。

内容是使用Grid 布局实现

  // 精选专题
  Column() {
    // 标题
    Row({ space: 10 }) {
      Text('精选专题')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
      Text('探索精彩壁纸专题')
        .fontSize(13)
        .fontColor('#ff888991')
    }
    .width('100%')
    .justifyContent(FlexAlign.Start)
    .margin({ bottom: 16 })

    // 内容
    Grid() {
      ForEach(LocalData.SpecialTopicData, (item: SpecialTopicType) => {
        GridItem() {
          SpecialTopicItemView({
            tag: item.tag,
            tagColor: item.tagColor,
            title: item.title,
            desc: item.desc,
            text: item.text,
            img: item.img
          })
        }
      })
    }
    .columnsTemplate('1fr 1fr')
    .columnsGap(16)
    .rowsGap(16)
    .width('100%')
  }
  .padding({ left: 16, right: 16, top: 6 })

LocalData.SpecialTopicData 对应的数据是

  /**
   * 精选专题
   */
  static readonly SpecialTopicData: SpecialTopicType[] = [
    {
      tag: '热门',
      tagColor: '#FF4081',
      title: '夏日清凉壁纸',
      desc: '精选清凉夏日壁纸',
      text: '夏日',
      img: "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/summer.jpg"
    },
    {
      tag: '精选',
      tagColor: '#2196F3',
      title: '梦幻星空系列',
      desc: '精选唯美星空壁纸',
      text: '星空',
      img: "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/starry.jpg"
    },
    {
      tag: '最新',
      tagColor: '#4CAF50',
      title: '城市夜景',
      desc: '精选城市&夜景壁纸',
      text: '城市夜景',
      img: "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/city.jpg"
    },
    {
      tag: '推荐',
      tagColor: '#FF9800',
      title: '自然风光精选',
      desc: '精选自然风光壁纸',
      text: '自然风光',
      img: "https://wsy997.obs.cn-east-3.myhuaweicloud.com/zrbz/generated.png"
    }
  ]

壁纸列表

最后就是壁纸列表了,考虑到壁纸列表存在比较多数据,因此这里使用 WaterFlowLazyForEach 实现页面渲染和分页加载

05-自然壁纸实战教程-首页-鸿蒙开发者社区

  //壁纸瀑布流
  WaterFlow() {
    LazyForEach(this.homeViewModel.imgList, (item: PixabayImage, index: number) => {
      FlowItem() {
        if (index % 6 === 0) {
          NativeAdPage()
        } else {
          WallpaperCard({ image: item })
        }
      }
      .width('100%')
      .onAppear(() => {
        if (index == (this.homeViewModel.imgList.totalCount() - 5)) {
          this.homeViewModel.onGetListEnd()
        }
      })
    })
  }
  .cachedCount(10)
  .columnsGap(16)
  .rowsGap(16)
  .width('100%')
  .height('100%')
  .columnsTemplate('1fr 1fr')
  .padding({ left: 16, right: 16 })
  .nestedScroll({
    scrollForward: NestedScrollMode.PARENT_FIRST,
    scrollBackward: NestedScrollMode.SELF_FIRST
  })

this.homeViewModel.imgList 来自于自己封装的 获取数据的类 entry/src/main/ets/viewModel/HomeViewModel.ets

import { getPhotoList } from "../services/photoServices";
import { LazyForEachDataSource } from "../utils/lazyForEachDataSource";
import { LocalData } from "../utils/localData";
import { PixabayImage, PixabayParams } from "../utils/types";
import json from "@ohos.util.json";

@ObservedV2
export class HomeViewModel {
  /**
   * 携带的参数
   */
  params: PixabayParams = {
    q: LocalData.CategoryData[0].value,
    page: 1,
    safesearch: true,
    per_page: 20,
  }
  /**
   * 瀑布流图片数据集合
   */
  @Trace
  imgList: LazyForEachDataSource<PixabayImage> = new LazyForEachDataSource

  /**
   * 获得瀑布流图片
   */
  async getList() {
    try {
      const res = await getPhotoList(this.params)
      if (res) {
        this.imgList.setArray(res.hits)
      }
    } catch (e) {
      console.log('瀑布流图片', json.stringify(e, null, 2))
    }
  }

  /**
   * 触底加载更多方法
   */
  async onGetListEnd() {
    this.params.page!++
    const res = await getPhotoList(this.params)
    if (res && res.hits.length > 0) {
      this.imgList.pushDataArr(res.hits)
    } else {
      this.params.page!--
    }
  }
}

如何获取资料

获取资料的途径,可以关注我们 官网的公众号 青蓝逐码 ,输入 项目名称 《自然壁纸》 即可获得以上资料。

关于我们

关于青蓝逐码组织

如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯甚至你想要做出一款属于自己的应用!欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

05-自然壁纸实战教程-首页-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
收藏
回复
举报
回复
    相关推荐