1 微信应用App概述

基于HarmonyOS 6 来实现类似于微信界面效果的应用。

微信界面主要包含4部分,即微信、联系人、发现、我,,本节所演示的例子也要实现这4部分。

1.1“微信”页面

“微信”页面是微信应用的首页,主要是展示联系人之间的沟通信息。如图所示是"微信"页面的效果图。

1.2“联系人”页面

“联系人”页面展示了用户所关联的联系人。

1.3“发现”页面

“发现”页面是微信进入其他子程序的入口。如图所示是“发现”页面的效果图。

1.4“我”页面

“我”页面展示用户个人信息的页面,如图所示是“我”页面的效果图。

2 实战:微信页面

这里会演示如何实现“微信”页面。“微信”页面主要是展示联系人的沟通记录列表。列表的每个项,都包含联系人头像、联系人名称、联系人聊天记录以及时间。

2.1 创建“微信”页面ChatPage

在pages创建ChatPage.ets作为"微信"页面。"微信"页面主要分为标题栏及沟通记录列表两部分。代码如下:

import {ChatItemStyle, WeChatTitle} from '../model/CommonStyle'
import { CONTACTS} from '../model/WeChatData'
import {Person} from '../model/Person'

@Component
export struct ChatPage {
  build() {
    Column() {
      // 标题
      WeChatTitle({ text: "微信" })
      // 列表
      List() {
        ForEach(CONTACTS, (item: Person) => {
          ListItem() {
            ChatItemStyle({
              weChatImage: item.weChatImage,
              weChatName: item.weChatName,
              chatInfo: item.chatInfo,
              time: item.time
            })
          }
        }, (item: Person) => item.id.toString())
      }
      .height('100%')
      .width('100%')
    }
  }
}

上述代码中,通过ForEach来遍历CONTACTS数组所返回的联系人数据,并生成ChatemStyle数据项。

2.2 定义联系人Person

联系人是用Person类作为表示。在ets目录下,创建model目录,并在该model目录创建Person.ets,代码如下:

export class Person {
  id: number = 0;
  weChatImage: string = "";
  weChatName: string = "";
  chatInfo: string = "";
  time: string = "";
}

Person内包含了头像、名称、聊天记录以及时间等信息。

2.3 定义联系人数据

在model目录下创建WeChatData.ets作为联系人数据。代码如下:

import { Person } from './Person'
export const CONTACTS: Person[] = [
  {
    id: 1,
    weChatImage: "person (1).jpg",
    weChatName: "李白", // 诗人名
    chatInfo: "天生我材必有用,千金散尽还复来。", // 《将进酒》诗句
    time: "09:45" // 随机时间
  },
  {
    id: 2,
    weChatImage: "person (2).jpg",
    weChatName: "杜甫", // 诗人名
    chatInfo: "安得广厦千万间,大庇天下寒士俱欢颜!", // 《茅屋为秋风所破歌》诗句
    time: "11:12" // 随机时间
  },
  {
    id: 3,
    weChatImage: "person (3).jpg",
    weChatName: "王维", // 诗人名
    chatInfo: "行到水穷处,坐看云起时。", // 《终南别业》诗句
    time: "08:30" // 随机时间
  },

  ...

{
    id: 15,
    weChatImage: "person (15).jpg",
    weChatName: "李煜", // 诗人名
    chatInfo: "问君能有几多愁?恰似一江春水向东流。", // 《虞美人·春花秋月何时了》词句
    time: "18:05" // 随机时间
  }
]
export const WE_CHAT_COLOR: string = "#ededed"

2.4 定义样式

在model目录下创建CommonStyle.ets作为样式类。在该类中定义标题的样式,代码如下:

import {WE_CHAT_COLOR} from './WeChatData';
import router from '@system.router';

@Component
export struct WeChatTitle {
  private text: string = "";

  build() {
    Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Text(this.text).fontSize('18fp').padding('20px')
    }.height('120px').backgroundColor(WE_CHAT_COLOR)
  }
}

// 定义沟通记录的样式
@Component
export struct ChatItemStyle {
  weChatImage: string = "";
  weChatName: string = "";
  chatInfo: string = "";
  time: string = "";

  build() {
    Column() {
      Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Image($rawfile(this.weChatImage)).width('120px').height('120px').margin({ left: '50px', right: "50px" })
        Column() {
          Text(this.weChatName).fontSize('16fp')
     Text(this.chatInfo).fontSize('12fp').width('620px').fontColor("#c2bec2").maxLines(1)
        }.alignItems(HorizontalAlign.Start).flexGrow(1)
        Text(this.time).fontSize('12fp')
          .margin({ right: "50px" }).fontColor("#c2bec2")
      }
      .height('180px')
      .width('100%')
      Row() {
        Text().width('190px').height('3px')
        Divider()
          .vertical(false)
          .color(WE_CHAT_COLOR)
          .strokeWidth('3px')
      }
      .height('3px')
      .width('100%')
    }
  }
}

// 定义沟通联系人的样式
@Component
export struct ContactItemStyle {
  private imageSrc: string = "";
  private text: string = "";

  build() {
    Column() {
      Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Image($rawfile(this.imageSrc)).width('100px').height('100px').margin({ left: '50px' })
        Text(this.text).fontSize('15vp').margin({ left: '40px' }).flexGrow(1)
      }
      .height('150px')
      .width('100%')
      Row() {
        Text().width('190px').height('3px')
        Divider()
          .vertical(false)
          .color(WE_CHAT_COLOR)
          .strokeWidth('3px')
      }
      .height('3px')
      .width('100%')
    }
  }
}

// 定义发现样式
@Component
export struct WeChatItemStyle {
  private imageSrc: string = "";
  private text: string = "";
  private arrow: string = "arrow.png"

  build() {
    Column() {
      Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Image($rawfile(this.imageSrc)).width('75px').height('75px').margin({ left: '50px' })
        Text(this.text).fontSize('15vp').margin({ left: '40px' }).flexGrow(1)
        Image($rawfile(this.arrow))
          .margin({ right: '40px' })
          .width('75px')
          .height('75px')
      }
      .height('150px')
      .width('100%')
    }.onClick(() => {
      if (this.text === "视频号") {
        router.push({ uri: 'pages/VideoPage' })
      }
    })
  }
}

// 定义分割线样式,用于发现页面
@Component
export struct MyDivider {
  private style: string = "
  build() {
    Row() {
      Divider()
        .vertical(false)
        .color(WE_CHAT_COLOR)
        .strokeWidth(this.style == "1" ? '3px' : '23px')
    }
    .height(this.style == "1" ? '3px' : '23px')
    .width('100%')
  }
}

上述代码中,CommonStyle中的ChatItemStyle 表示定义沟通记录的样式,实现效果如图:

3 实战:“联系人”页面

这里演示如何实现“联系人”页面。“联系人”页面主要是展示联系人列表,列表的每个项都包含联系人头像、联系人名称。因此,实现方式与“微信”页面类似。

3.1 创建“联系人”页面ContactPage

在pages创建ContactPage.ets作为“联系人”页面。“联系人”页面主要分为标题栏及联系人列表。代码如下:

import {ContactItemStyle, WeChatTitle} from '../model/CommonStyle'
import {Person} from '../model/Person'
import { CONTACTS, WE_CHAT_COLOR} from '../model/WeChatData'

@Component
export struct ContactPage {
  build() {
    Column() {
      // 标题
      WeChatTitle({ text: "通讯录" })

      // 列表
      Scroll() {
        Column() {
          // 固定列表
          ContactItemStyle({ imageSrc: "new_friend.png", text: "新的朋友" })
          ContactItemStyle({ imageSrc: "group.png", text: "群聊" })
          ContactItemStyle({ imageSrc: "biaoqian.png", text: "标签" })
          ContactItemStyle({ imageSrc: "gonzh.png", text: "公众号" })

          // 企业联系人
          Text("      我的企业及企业联系人").fontSize('12fp').backgroundColor(WE_CHAT_COLOR).height('80px').width('100%')
          ContactItemStyle({ imageSrc: "qiye.png", text: "企业微信联系人" })

          // 微信好友
          Text("      我的微信好友").fontSize('12fp').backgroundColor(WE_CHAT_COLOR).height('80px').width('100%')
          List() {
            ForEach(CONTACTS, (item: Person) => {
              ListItem() {
                ContactItemStyle({ imageSrc: item.weChatImage, text: item.weChatName })
              }
            }, (item: Person) => item.id.toString())
          }
        }
      }

    }.alignItems(HorizontalAlign.Start)
    .width('100%')
    .height('100%')
  }
}

联系人列表又细分为3个部分:分类、企业联系人、微信好友。

3.2 定义样式

在CommonStyle中定义联系人的样式ContactItemStyle,实现效果如图:

4 实战:“发现”页面

本节演示如何实现“发现”页面。“发现”页面是微信进入其他子程序的入口,每个子程序本质上地是一个列表项。

4.1创建发现页面DiscoveryPage

“发现”页面主要分为标题栏及子程序列表,在pages创建DiscoveryPage.ets作为“发现”页面。代码如下:

import {WeChatItemStyle, MyDivider, WeChatTitle} from '../model/CommonStyle'

@Component
export struct DiscoveryPage {
  build() {
    Column() {
      // 标题
      WeChatTitle({ text: "发现" })

      // 列表
      WeChatItemStyle({ imageSrc: "moments.png", text: "朋友圈" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "shipinghao.png", text: "视频号" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "zb.png", text: "直播" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "sys.png", text: "扫一扫" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "yyy.png", text: "摇一摇" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "kyk.png", text: "看一看" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "souyisou.png", text: "搜一搜" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "fujin.png", text: "附近" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "gw.png", text: "购物" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "game.png", text: "游戏" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "xcx.png", text: "小程序" })
      MyDivider()
    }.alignItems(HorizontalAlign.Start)
    .width('100%')
    .height('100%')
  }
}

子程序用WeChatltemStyle定义样式,并通过MyDivider来进行分割。

4.2 定义样式

在CommonStyle中定义子程序的样式WeChatItemStyle,子程序主要由3部分组成:图标、名称及箭头。同时定义分割线样式MyDivider。实现效果如下:

5 实战:“我”页面

这里演示如何实现“我”页面。“我”页面是展示用户个人的信息。在pages创建MyPage.ets作为“我”页面。“我”页面主要分为用户信息部分及菜单列表。代码如下:

import {WeChatItemStyle, MyDivider} from '../model/CommonStyle'

@Component
export struct MyPage {
  private imageTitle: string = "title.png"

  build() {
    Column() {
      // 用户信息部分
      Image($rawfile(this.imageTitle)).height(144).width('100%')

      // 列表
      WeChatItemStyle({ imageSrc: "pay.png", text: "服务" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "favorites.png", text: "收藏" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "moments2.png", text: "朋友圈" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "video.png", text: "视频号" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "card.png", text: "卡包" })
      MyDivider({ style: '1' })
      WeChatItemStyle({ imageSrc: "emoticon.png", text: "表情" })
      MyDivider()

      WeChatItemStyle({ imageSrc: "setting.png", text: "设置" })
      MyDivider()
    }.alignItems(HorizontalAlign.Start)
    .width('100%')
    .height('100%')
  }
}

与“发现”页面的子程序类似,“我”页面同样也是使用了WeChatltemStyle、MyDivider的样式。

6 实战:组装所有页面

在应用的Index页面,需要将微信、联系人、发现、我4个页面组装在一起,并实现自由切换此时,就可以使用HarmonyOS的Tabs组件作为导航栏。

6.1 Tabs组件作为导航栏

Tabs组件作为导航栏,Index.ets文件中,代码实现如下。

import { ChatPage } from './ChatPage'
import { ContactPage } from './ContactPage'
import { DiscoveryPage } from './DiscoveryPage'
import { MyPage } from './MyPage'

@Entry
@Component
struct Index {
  @Provide currentPage: number = 0
  @State currentIndex: number = 0;

  build() {
    Column() {
      Tabs({
        index: this.currentIndex,
        barPosition: BarPosition.End
      }) {
        TabContent() {
          ChatPage()
        }
        .tabBar(this.TabBuilder('微信', 0, $r('app.media.wechat2'), $r('app.media.wechat1')))

        TabContent() {
          ContactPage()
        }
        .tabBar(this.TabBuilder('联系人', 1, $r('app.media.contacts2'), $r('app.media.contacts1')))

        TabContent() {
          DiscoveryPage()
        }
        .tabBar(this.TabBuilder('发现', 2, $r('app.media.find2'), $r('app.media.find1')))

        TabContent() {
          MyPage()
        }
        .tabBar(
          this.TabBuilder('我', 3, $r('app.media.me2'), $r('app.media.me1'))
        )
      }
      .barMode(BarMode.Fixed)
      .onChange((index: number) => {
        this.currentIndex = index;
      })
    }
  }
}

对于底部导航栏,一般作为应用主页面功能区分,为了提升用户体验,我们会组合文字以及对应义图标表示页签内容。在这种情况下,需要自定义导航页签的样式,在Index.ets中完成。代码如下:

@Builder

TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
  Column() {
    Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
      .size({ width: 25, height: 25 })
    Text(title)
      .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
  }
  .width('100%')
  .height(50)
  .justifyContent(FlexAlign.Center)
}

导航栏在选中时会呈现出高亮的效果,如图所示。

6.2 Swiper组件实现页面滑动

除了通过底部导航栏实现页面切换外,还可以使用Swiper组件来左右滑动页面从而实现页面切换。在Index.ets文件中定义。代码如下:

@Component
struct HomeTopPage {
  @Consume currentPage: number

  build() {
    Swiper() {
      ChatPage()
      ContactPage()
      DiscoveryPage()
      MyPage()
    }
    .onChange((index: number) => {
      this.currentPage = index
    })
    .index(this.currentPage)
    .loop(false)
    .indicator(false)
    .width('100%')
    .height('100%')
  }
}

7 微信APP项目小结

本项目基于HarmonyOS6提供的组件实现了类似于微信界面效果的应用,该应用主要使用了Flex、Tabs、Column、TabBuilder、Image、Text、Swiper等组件,希望开发者能掌握这些组件在实际开发中的应用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐