基于HarmonyOS Next的运动社交应用开发全攻略

基于HarmonyOS Next的运动社交应用开发全攻略

从零开始构建你的第一个运动社交应用

最近在健身房认识了不少志同道合的朋友,突然想到如果能有个专门为运动爱好者打造的社交平台该多好。正好HarmonyOS Next提供了强大的开发工具和生态支持,今天我们就来一起实现这个想法。

开发前的准备工作

首先打开DevEco Studio,创建一个新项目。这里我建议选择"Empty Ability"模板,语言选择ArkTS,兼容模式选择Stage模型。这样能确保我们使用最新的开发范式。

// 应用入口文件 EntryAbility.ts
import UIAbility from **********';
import window from **********';

export default class EntryAbility extends UIAbility {
  // 应用启动时初始化
  onCreate() {
    console.log('运动社交应用启动啦!');
  }

  // 创建主窗口
  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/HomePage', (err) => {
      if (err) {
        console.error('加载页面时出了点问题:', err);
        return;
      }
      console.log('首页加载完成!');
    });
  }
}

用户系统的设计与实现

登录注册功能开发

运动社交应用最基础的就是用户系统了。我们可以使用AppGallery Connect提供的认证服务,省去自己搭建用户系统的麻烦。

先在AGC控制台开启认证服务,支持手机号、邮箱和匿名登录。然后在项目中集成AGC SDK:

// 用户服务 UserService.ts
import agconnect from '@hw-agconnect/api-ohos';
import '@hw-agconnect/auth-ohos';

class UserService {
  // 初始化AGC认证
  static init() {
    agconnect.instance().init(this.context);
    console.log('用户服务初始化完成');
  }

  // 手机号登录
  static async loginWithPhone(phone: string, code: string) {
    try {
      const credential = agconnect.auth.PhoneAuthProvider.credentialWithVerifyCode(
        phone,
        code
      );
      await agconnect.auth().signIn(credential);
      console.log('手机号登录成功');
      return true;
    } catch (error) {
      console.error('登录遇到问题:', error);
      return false;
    }
  }

  // 获取当前用户
  static getCurrentUser() {
    return agconnect.auth().currentUser;
  }
}

export default UserService;

个人资料页面设计

有了登录功能,接下来设计个人资料页面。这里我们可以展示用户的运动数据和基本信息。

// 个人资料页 ProfilePage.ets
@Component
struct UserAvatar {
  @Prop avatarUrl: string = 'common/default_avatar.png'

  build() {
    Image(this.avatarUrl)
      .width(80)
      .height(80)
      .borderRadius(40)
      .margin(10)
  }
}

@Entry
@Component
struct ProfilePage {
  @State userInfo: {
    nickname: string,
    level: number,
    totalDistance: number
  } = {
    nickname: '运动达人',
    level: 3,
    totalDistance: 156.8
  }

  build() {
    Column() {
      // 用户头像区域
      UserAvatar({ avatarUrl: 'common/user_avatar.jpg' })
      
      // 基本信息
      Text(this.userInfo.nickname)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      // 运动数据展示
      Row() {
        Column() {
          Text('Lv.' + this.userInfo.level)
            .fontSize(18)
          Text('运动等级')
            .fontSize(12)
            .fontColor('#666')
        }
        .margin({ right: 20 })
        
        Column() {
          Text(this.userInfo.totalDistance + 'km')
            .fontSize(18)
          Text('累计里程')
            .fontSize(12)
            .fontColor('#666')
        }
      }
      .margin({ top: 15 })
      
      // 编辑资料按钮
      Button('编辑资料')
        .width('60%')
        .margin({ top: 30 })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

运动数据记录功能

健康数据接入

HarmonyOS提供了完善的健康数据接口,我们可以直接获取用户的运动数据。

// 运动服务 SportService.ts
import { health } from **********';

class SportService {
  // 请求健康数据权限
  static async requestHealthPermission() {
    try {
      const permissions = [
        'ohos.permission.health.READ_HEALTH_DATA',
        'ohos.permission.health.WRITE_HEALTH_DATA'
      ];
      await abilityAccessCtrl.requestPermissionsFromUser(permissions);
      console.log('健康数据权限获取成功');
    } catch (error) {
      console.error('获取权限时出错:', error);
    }
  }

  // 获取今日步数
  static async getTodaySteps() {
    try {
      const now = new Date();
      const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
      
      const result = await health.getHealthData({
        startTime: startOfDay.getTime(),
        endTime: now.getTime(),
        dataType: 'STEP_COUNT'
      });
      
      return result.length > 0 ? result[0].value : 0;
    } catch (error) {
      console.error('获取步数失败:', error);
      return 0;
    }
  }
}

export default SportService;

运动记录界面

设计一个美观的运动数据展示界面:

// 运动记录页 SportRecordPage.ets
@Component
struct StepCounter {
  @Prop currentSteps: number
  @Prop targetSteps: number = 10000

  build() {
    Column() {
      // 环形进度条
      Stack() {
        Circle({ width: 180, height: 180 })
          .strokeWidth(10)
          .stroke('#eee')
        
        Circle({ width: 180, height: 180 })
          .strokeWidth(10)
          .stroke('#ff5a5f')
          .sweepAngle(this.currentSteps / this.targetSteps * 360)
      }
      
      // 步数显示
      Text(this.currentSteps.toString())
        .fontSize(36)
        .margin({ top: 20 })
      
      Text('/ ' + this.targetSteps + ' 步')
        .fontSize(16)
        .fontColor('#666')
    }
    .width('100%')
    .margin({ top: 30 })
  }
}

@Entry
@Component
struct SportRecordPage {
  @State steps: number = 0

  aboutToAppear() {
    SportService.getTodaySteps().then(steps => {
      this.steps = steps;
    });
  }

  build() {
    Column() {
      StepCounter({ currentSteps: this.steps })
      
      // 其他运动数据
      Row() {
        Column() {
          Text('5.6')
            .fontSize(24)
          Text('公里')
            .fontSize(14)
            .fontColor('#666')
        }
        .margin({ right: 30 })
        
        Column() {
          Text('420')
            .fontSize(24)
          Text('千卡')
            .fontSize(14)
            .fontColor('#666')
        }
      }
      .margin({ top: 40 })
      
      // 开始运动按钮
      Button('开始记录运动')
        .type(ButtonType.Capsule)
        .width('80%')
        .height(50)
        .margin({ top: 50 })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

社交功能开发

动态发布功能

运动社交的核心当然是分享功能了。我们先实现动态发布:

// 动态服务 PostService.ts
import { clouddb } from '@hw-agconnect/database-ohos';

class PostService {
  private static cloudDBZone: clouddb.CloudDBZone;

  // 初始化云数据库
  static async init() {
    const config = new clouddb.CloudDBZoneConfig(
      'SportSocialZone',
      clouddb.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
      clouddb.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC
    );
    
    this.cloudDBZone = await clouddb.CloudDBZone.open(config);
    await clouddb.CloudDBZone.registerObjectClass(this.cloudDBZone, 'Post');
    console.log('动态数据库初始化完成');
  }

  // 发布新动态
  static async createPost(content: string, images?: string[]) {
    const post = {
      id: generateId(),
      userId: UserService.getCurrentUser().uid,
      content,
      images: images || [],
      likes: 0,
      comments: 0,
      createTime: new Date().getTime()
    };
    
    try {
      await this.cloudDBZone.executeUpsert('Post', [post]);
      console.log('动态发布成功');
      return true;
    } catch (error) {
      console.error('发布动态失败:', error);
      return false;
    }
  }
}

export default PostService;

动态列表展示

// 动态列表页 PostListPage.ets
@Component
struct PostItem {
  @Prop post: {
    id: string,
    userId: string,
    content: string,
    likes: number,
    comments: number
  }

  @State isLiked: boolean = false

  build() {
    Column() {
      // 用户信息栏
      Row() {
        UserAvatar({ avatarUrl: 'common/user_avatar.jpg' })
        
        Column() {
          Text('运动达人')
            .fontSize(16)
          Text('2小时前')
            .fontSize(12)
            .fontColor('#999')
        }
        .margin({ left: 10 })
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
      
      // 动态内容
      Text(this.post.content)
        .fontSize(16)
        .margin({ top: 10, bottom: 10 })
        .width('100%')
      
      // 互动区域
      Row() {
        Image(this.isLiked ? 'common/liked.png' : 'common/like.png')
          .width(20)
          .height(20)
          .onClick(() => {
            this.isLiked = !this.isLiked;
          })
        Text(this.post.likes + (this.isLiked ? 1 : 0) + '')
          .fontSize(14)
          .margin({ left: 5 })
        
        Image('common/comment.png')
          .width(20)
          .height(20)
          .margin({ left: 20 })
        Text(this.post.comments + '')
          .fontSize(14)
          .margin({ left: 5 })
      }
      .width('100%')
      .margin({ top: 10 })
    }
    .padding(15)
    .borderRadius(10)
    .backgroundColor('#fff')
    .margin({ bottom: 10 })
    .width('100%')
  }
}

@Entry
@Component
struct PostListPage {
  @State posts: any[] = []

  aboutToAppear() {
    this.loadPosts();
  }

  async loadPosts() {
    // 这里实际应该从云数据库获取数据
    this.posts = [
      {
        id: '1',
        userId: 'user1',
        content: '今天晨跑5公里,感觉特别棒!',
        likes: 12,
        comments: 3
      },
      {
        id: '2',
        userId: 'user2',
        content: '健身房打卡第三天,继续坚持!',
        likes: 8,
        comments: 2
      }
    ];
  }

  build() {
    Column() {
      List({ space: 10 }) {
        ForEach(this.posts, (post) => {
          ListItem() {
            PostItem({ post: post })
          }
        })
      }
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .padding(10)
    .backgroundColor('#f5f5f5')
  }
}

应用优化与发布

性能优化小技巧

在实际开发中,我发现几个提升应用流畅度的好方法:

  1. 图片懒加载:对于动态中的图片,可以使用LazyForEach实现滚动时加载
  2. 数据缓存:将用户信息和常用数据缓存在本地,减少网络请求
  3. 组件复用:像用户头像这样的通用组件要单独封装
// 优化后的图片加载组件
@Component
struct LazyImage {
  @Prop src: string
  @State loaded: boolean = false

  build() {
    Stack() {
      if (!this.loaded) {
        Progress()
          .width(50)
          .height(50)
      }
      
      Image(this.src)
        .onComplete(() => {
          this.loaded = true;
        })
        .opacity(this.loaded ? 1 : 0)
    }
    .width('100%')
    .aspectRatio(1)
  }
}

准备上架应用市场

开发完成后,我们需要:

  1. 在AGC控制台完善应用信息
  2. 生成签名证书
  3. 构建发布版本
  4. 提交审核

记得在config.json中配置好所有需要的权限:

{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.health.READ_HEALTH_DATA",
        "reason": "用于记录您的运动数据"
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "连接网络服务"
      }
    ]
  }
}

写在最后

通过这个项目,我们完整实现了一个运动社交应用的主要功能。HarmonyOS Next的开发体验真的很不错,特别是ArkTS语言的简洁性和AGC服务的便利性,大大提高了开发效率。

这个应用还有很多可以扩展的地方,比如:

  • 添加运动轨迹记录
  • 实现好友系统
  • 开发运动成就体系
  • 增加运动数据分析

希望这篇教程能给你带来启发,如果有任何问题,欢迎在评论区交流讨论。期待看到你开发的运动社交应用!

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-09 13:05
TMD找工作本来就烦,这东西什么素质啊😡
Beeee0927:hr是超雄了,不过也是有道理的
点赞 评论 收藏
分享
能干的三文鱼刷了10...:公司可能有弄嵌入式需要会画pcb的需求,而且pcb能快速直观看出一个人某方面的实力。看看是否有面试资格。问你问题也能ai出来,pcb这东西能作假概率不高
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务