应用概述

这个实时新闻推送应用将提供多种新闻类别的实时更新,支持个性化订阅、新闻收藏和推送通知功能。

开发步骤

1. 创建项目

  1. 打开CodeGenie IDE
  2. 选择"File" > "New" > "HarmonyOS Project"
  3. 输入项目名称"NewsFlash"
  4. 选择API版本为HarmonyOS 5
  5. 点击"Finish"

2. 配置权限

config.json中添加必要权限:

{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.RECEIVER_STARTUP_COMPLETED"
      },
      {
        "name": "ohos.permission.NOTIFICATION_CONTROLLER"
      },
      {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
      }
    ]
  }
}

3. 设计数据模型

resources/base/profile目录下创建news_data.json:

{
  "categories": [
    {"id": 1, "name": "头条", "subscribed": true},
    {"id": 2, "name": "科技", "subscribed": false},
    {"id": 3, "name": "财经", "subscribed": false},
    {"id": 4, "name": "体育", "subscribed": true},
    {"id": 5, "name": "娱乐", "subscribed": false}
  ],
  "news": [
    {
      "id": 1,
      "title": "鸿蒙系统5.0正式发布",
      "content": "华为今日正式发布鸿蒙HarmonyOS 5.0系统,带来多项创新功能...",
      "category": "科技",
      "time": "2023-08-10 09:30",
      "source": "华为新闻",
      "image": "media/harmonyos5.jpg",
      "isBreaking": true
    }
  ]
}

4. 创建主界面布局

resources/base/layout目录下创建main_page.xml:

<TabView
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent">
    
    <TabBar
        ohos:width="match_parent"
        ohos:height="50vp"
        ohos:orientation="horizontal">
        <TextTab>推荐</TextTab>
        <TextTab>订阅</TextTab>
        <TextTab>收藏</TextTab>
    </TabBar>
    
    <TabContent>
        <!-- 推荐新闻列表 -->
        <RefreshContainer ohos:id="$+id:refresh_container">
            <ListContainer ohos:id="$+id:news_list"/>
        </RefreshContainer>
    </TabContent>
    
    <TabContent>
        <!-- 订阅管理 -->
        <ListContainer ohos:id="$+id:subscription_list"/>
    </TabContent>
    
    <TabContent>
        <!-- 收藏新闻 -->
        <ListContainer ohos:id="$+id:favorites_list"/>
    </TabContent>
</TabView>

5. 实现主界面逻辑

src/main/ets/MainAbility目录下创建MainPage.ets:

import newsData from '../../resources/base/profile/news_data.json';
import { NewsItem, NewsCategory } from '../common/model';

@Entry
@Component
struct MainPage {
  @State newsList: Array<NewsItem> = newsData.news
  @State categories: Array<NewsCategory> = newsData.categories
  @State favorites: Array<NewsItem> = []
  @State activeTab: number = 0
  @State refreshing: boolean = false

  // 模拟从网络获取最新新闻
  fetchLatestNews() {
    this.refreshing = true
    // 实际开发中替换为真实的API调用
    setTimeout(() => {
      this.newsList = [...this.generateMockNews(), ...this.newsList]
      this.refreshing = false
    }, 1500)
  }

  // 生成模拟新闻数据
  generateMockNews(): Array<NewsItem> {
    const mockTitles = [
      "我国成功发射新一代通信卫星",
      "人工智能大会在上海开幕",
      "新能源汽车销量创新高",
      "世界杯预选赛亚洲区开战"
    ]
    return mockTitles.map((title, index) => ({
      id: Date.now() + index,
      title,
      content: `${title}相关内容...`,
      category: ["科技", "科技", "财经", "体育"][index],
      time: new Date().toLocaleString(),
      source: ["新华社", "央视新闻", "财经日报", "体育在线"][index],
      image: "",
      isBreaking: index % 3 === 0
    }))
  }

  build() {
    Tabs({ barPosition: BarPosition.Start }) {
      TabContent() {
        Refresh({ refreshing: this.refreshing }) {
          List() {
            ForEach(this.newsList, (item: NewsItem) => {
              ListItem() {
                NewsCard({ news: item })
              }
            })
          }
          .width('100%')
          .divider({ strokeWidth: 1, color: '#f0f0f0' })
        }
        .onRefresh(() => {
          this.fetchLatestNews()
        })
      }.tabBar('推荐')
      
      TabContent() {
        List() {
          ForEach(this.categories, (category: NewsCategory) => {
            ListItem() {
              Row() {
                Text(category.name)
                  .fontSize(18)
                  .layoutWeight(1)
                
                Toggle({ type: ToggleType.Checkbox, isOn: category.subscribed })
                  .onChange((isOn: boolean) => {
                    category.subscribed = isOn
                  })
              }
              .width('100%')
              .padding(15)
            }
          })
        }
      }.tabBar('订阅')
      
      TabContent() {
        if (this.favorites.length === 0) {
          Column() {
            Image($r('app.media.empty_favorites'))
              .width(150)
              .height(150)
              .margin({ bottom: 20 })
            
            Text('暂无收藏内容')
              .fontSize(16)
              .fontColor(Color.Gray)
          }
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)
        } else {
          List() {
            ForEach(this.favorites, (item: NewsItem) => {
              ListItem() {
                NewsCard({ news: item })
              }
            })
          }
        }
      }.tabBar('收藏')
    }
    .barWidth('100%')
    .barHeight(50)
    .onChange((index: number) => {
      this.activeTab = index
    })
  }
}

@Component
struct NewsCard {
  @Prop news: NewsItem
  @State isFavorite: boolean = false

  build() {
    Column() {
      if (this.news.isBreaking) {
        Row() {
          Image($r('app.media.breaking'))
            .width(20)
            .height(20)
            .margin({ right: 5 })
          
          Text('快讯')
            .fontSize(14)
            .fontColor(Color.Red)
        }
        .width('100%')
        .margin({ bottom: 5 })
      }
      
      Row() {
        Column() {
          Text(this.news.title)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 5 })
          
          Text(`${this.news.source} · ${this.news.time}`)
            .fontSize(12)
            .fontColor(Color.Gray)
        }
        .layoutWeight(1)
        
        Icon(this.isFavorite ? $r('app.media.favorite_filled') : $r('app.media.favorite'))
          .width(24)
          .height(24)
          .onClick(() => {
            this.isFavorite = !this.isFavorite
            // 这里应更新收藏列表
          })
      }
      
      if (this.news.image) {
        Image(this.news.image)
          .width('100%')
          .height(150)
          .margin({ top: 10 })
          .objectFit(ImageFit.Cover)
      }
      
      Text(this.news.content.length > 60 ? 
           this.news.content.substring(0, 60) + '...' : 
           this.news.content)
        .fontSize(14)
        .margin({ top: 10 })
    }
    .width('100%')
    .padding(15)
    .onClick(() => {
      router.push({
        url: 'pages/NewsDetail',
        params: { newsId: this.news.id }
      })
    })
  }
}

6. 创建新闻详情页

resources/base/layout目录下创建news_detail.xml:

<ScrollView
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent">
    
    <DirectionalLayout
        ohos:width="match_parent"
        ohos:height="match_content"
        ohos:orientation="vertical"
        ohos:padding="20vp">
        
        <Text
            ohos:id="$+id:title"
            ohos:width="match_parent"
            ohos:height="wrap_content"
            ohos:text_size="20fp"
            ohos:text_alignment="center"
            ohos:margin="10vp"/>
            
        <Text
            ohos:id="$+id:meta"
            ohos:width="match_parent"
            ohos:height="wrap_content"
            ohos:text_size="14fp"
            ohos:text_alignment="center"
            ohos:margin="5vp"/>
            
        <Image
            ohos:id="$+id:image"
            ohos:width="match_parent"
            ohos:height="200vp"
            ohos:margin="10vp"
            ohos:visibility="visible"/>
            
        <Text
            ohos:id="$+id:content"
            ohos:width="match_parent"
            ohos:height="wrap_content"
            ohos:text_size="16fp"
            ohos:margin="10vp"/>
            
        <Button
            ohos:id="$+id:share_btn"
            ohos:width="match_parent"
            ohos:height="50vp"
            ohos:text="分享新闻"
            ohos:margin="10vp"/>
    </DirectionalLayout>
</ScrollView>

7. 实现详情页逻辑

创建NewsDetail.ets:

import newsData from '../../resources/base/profile/news_data.json';
import { NewsItem } from '../common/model';

@Entry
@Component
struct NewsDetail {
  @State news: NewsItem = newsData.news[0]
  @State isFavorite: boolean = false

  onInit() {
    const params = router.getParams() as Record<string, number>
    if (params && params.newsId) {
      const foundNews = newsData.news.find(item => item.id === params.newsId)
      if (foundNews) {
        this.news = foundNews
      }
    }
  }

  build() {
    Column() {
      Text(this.news.title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)
        .margin(10)
      
      Text(`${this.news.source} · ${this.news.time}`)
        .fontSize(14)
        .fontColor(Color.Gray)
        .textAlign(TextAlign.Center)
        .margin({ bottom: 15 })
      
      if (this.news.image) {
        Image(this.news.image)
          .width('100%')
          .height(200)
          .objectFit(ImageFit.Cover)
          .margin({ bottom: 15 })
      }
      
      Text(this.news.content)
        .fontSize(16)
        .margin({ bottom: 20 })
      
      Row() {
        Button('分享')
          .width('45%')
          .height(45)
          .onClick(() => {
            this.shareNews()
          })
        
        Button(this.isFavorite ? '已收藏' : '收藏')
          .width('45%')
          .height(45)
          .margin({ left: '10%' })
          .backgroundColor(this.isFavorite ? '#f0f0f0' : '#1890ff')
          .onClick(() => {
            this.isFavorite = !this.isFavorite
            // 这里应更新收藏列表
          })
      }
      .width('100%')
      .margin({ top: 10 })
    }
    .width('100%')
    .padding(15)
  }

  shareNews() {
    // 实现分享功能
    prompt.showToast({
      message: '分享功能已触发',
      duration: 2000
    })
  }
}

8. 添加类型定义

src/main/ets/common目录下创建model.ets:

export interface NewsItem {
  id: number;
  title: string;
  content: string;
  category: string;
  time: string;
  source: string;
  image: string;
  isBreaking: boolean;
}

export interface NewsCategory {
  id: number;
  name: string;
  subscribed: boolean;
}

9. 实现推送服务

创建src/main/ets/services/PushService.ets:

import { NewsItem } from '../common/model';
import notification from '@ohos.notification';

export class PushService {
  static subscribeToCategory(categoryId: number) {
    // 实际开发中这里应该调用推送服务的订阅API
    console.log(`已订阅分类ID: ${categoryId}`)
  }

  static unsubscribeFromCategory(categoryId: number) {
    // 实际开发中这里应该调用推送服务的取消订阅API
    console.log(`已取消订阅分类ID: ${categoryId}`)
  }

  static showNotification(news: NewsItem) {
    notification.publish({
      id: news.id,
      content: {
        title: news.title,
        text: news.content.length > 50 ? news.content.substring(0, 50) + '...' : news.content
      },
      tapAction: {
        want: {
          bundleName: 'com.example.newsflash',
          abilityName: 'MainAbility',
          uri: `news://detail?id=${news.id}`
        }
      }
    }).then(() => {
      console.log('通知发送成功')
    }).catch(err => {
      console.error('通知发送失败:', err)
    })
  }

  static checkForBreakingNews() {
    // 定时检查突发新闻
    setInterval(() => {
      // 实际开发中这里应该调用API检查最新新闻
      const mockBreakingNews = {
        id: Date.now(),
        title: "突发: " + ["地震快报", "重大政策", "国际事件", "股市异动"][Math.floor(Math.random() * 4)],
        content: "点击查看详情...",
        category: "头条",
        time: new Date().toLocaleString(),
        source: "新闻快讯",
        image: "",
        isBreaking: true
      }
      this.showNotification(mockBreakingNews)
    }, 3600000) // 每小时检查一次
  }
}

10. 添加路由配置

src/main/resources/base/profile/main_pages.json中添加:

{
  "src": [
    "pages/MainPage",
    "pages/NewsDetail"
  ]
}

功能扩展建议

  1. ​集成新闻API​​:连接真实的新闻API如NewsAPI、腾讯新闻API等
  2. ​离线阅读​​:实现新闻缓存功能,支持离线阅读
  3. ​夜间模式​​:添加暗色主题支持
  4. ​字体调整​​:允许用户调整新闻阅读字体大小
  5. ​评论功能​​:添加新闻评论和互动功能
  6. ​视频新闻​​:支持视频新闻播放
  7. ​本地新闻​​:基于位置服务的本地新闻推送
  8. ​AI摘要​​:使用AI生成新闻摘要

测试与发布

  1. 在CodeGenie中使用预览器测试应用界面
  2. 测试推送通知功能
  3. 测试网络请求和错误处理
  4. 在真机设备上测试所有功能
  5. 使用HUAWEI AppGallery Connect进行应用签名和发布

这个实时新闻推送小程序将帮助用户随时获取最新资讯,通过鸿蒙系统的分布式能力,用户可以在手机、平板、智能手表等多种设备上接收和阅读新闻。

Logo

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

更多推荐