#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS) 原创

中软HOS小鸿
发布于 2021-12-15 09:12
浏览
7收藏

作者:梁青松

【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】

简介

本来计划要做一个本地数据库存储相关的项目,但是官方的小伙伴给我说,ArkUI数据库目前不支持最新的模拟器。所以只能另想其他的,想了大概一周,期间也在调研技术能否实现,最终定了这个项目,真正的敲代码开发大概就用了一天,最难的就是想法和界面设计。

不得不感慨新框架真的方便、真的好用!期待下个版本能完善组件和API的细节。也希望鸿蒙越来越好。。。。。

界面搭建基于ArkUI中TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:

基于TS扩展的声明式开发范式1基于TS扩展的声明式开发范式2

项目说明

使用系统自带的网络请求框架,根据Tab的类型切换请求对应的数据。列表支持下拉刷新、上拉加载更多。模拟登录效果,根据输入框来确定按钮是否启用,登录成功后,显示登录头像和用户信息。

ArKUI系列文章

《HarmonyOS ArkUI之仿微信朋友圈图片预览》

《HarmonyOS ArkUI之仿微信图片选择》

《HarmonyOS ArkUI之自定义组件侧滑菜单(JS)》

《HarmonyOS ArkUI之聊天列表滑动删除(TS)》

《HarmonyOS ArkUI之开发基础(网络请求)》

《HarmonyOS 项目实战之新闻头条(ArkUI-TS)》

效果演示

在远程模拟器录制的,效果不佳 ~

#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区 #星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区

功能解析

1 首页

首页从上至下分为三个部分:

  1. 标题栏

  2. Tab标签

  3. 数据列表

#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区

1.1 标题栏

布局很简单,使用Row布局包裹:ImageSwiper(搜索框中文字上下切换)

(部分代码)

......
  // 标题栏
  @Builder CustomTitleBar() {
    Row() {
      // 头像  
      Image(this.isLogin ? $r('app.media.ic_ldd_headpic') : $r('app.media.ic_default_headpic'))
        .width(30)
        .height(30)
        .borderRadius(15)
        .margin({ right: 10 })
        .onClick(() => {
          this.openSideMenu()
        })
		
      // 搜索框  
      Row() {
        // 搜索图标  
        Image($r('app.media.ic_search'))
          .width(15).height(15)
          .margin({ left: 10 })
		
        // 视图上下切换
        Swiper() {
          ForEach(this.listSearch, item => {
            Text(item)
              .height('100%')
              .fontSize(12)
              .fontColor('#505050')
              .margin({ left: 10 })
          }, item => item)
        }
        .vertical(true) // 方向:纵向
        .autoPlay(true) // 自动播放
        .indicator(false) // 隐藏指示器
        .interval(3000) // 切换间隔时间3秒
      }
      .layoutWeight(1)
      .height('100%')
      .backgroundColor('#F1F1F1')
      .borderRadius(15)
    }
    .width('100%')
    .height(50)
    .backgroundColor(Color.White)
    .padding({ top: 10, bottom: 10, left: 15, right: 15 })
  }
......

1.2 Tab标签

这个也比较简单,根据屏幕宽度、tab标签的总数量,就能得出tabItem的宽度。底部设置的指示器,点击tab根据 index(当前索引) * itemWithd(每个tab的宽度)设置属性动画,切换效果就可以了。

import { TabModel,getTabList} from '../../model/tabModel.ets';
import display from '@ohos.display';
@Component
export struct HomeTabs {
  // Tab数据  
  private listTab = getTabList()
  // tabItem平均宽度  
  @State tabIndicatorWidth: number = 152
  // 指示器  
  @State tabIndex: number = 0
  // 对外暴露的方法 
  private tabClick: (item: TabModel) => void

  private aboutToAppear() {
    display.getDefaultDisplay((err, data) => {
      if (!err) {
        // 获取tabItem平均宽度   
        this.tabIndicatorWidth = data.width / this.listTab.length
      }
    })
  }

  build() {
    Column(){
      Stack({ alignContent: Alignment.Bottom }) {
        // tab内容  
        Row() {
          ForEach(this.listTab, item => {
            Button() {
              Text(item.name)
                .fontSize(this.tabIndex == item.id ? 15 : 13) // 根据当前选中改变字体大小
                .fontColor(this.tabIndex == item.id ? $r('app.color.app_theme') : '#000000')// 根据当前选中改变字体颜色
            }
            .layoutWeight(1)
            .height(35)
            .type(ButtonType.Normal)
            .backgroundColor(Color.White)
            .onClick(() => {
              this.tabIndex = item.id // 更新索引
              this.tabClick(item) // 提供给外部调用
            })
          }, item => item.tabType)
        }.height(35)

        // 指示器  
        Row() {
          Divider()
            .width(`${this.tabIndicatorWidth}px`) // 平均宽度
            .strokeWidth(3)
            .color($r('app.color.app_theme'))
            .lineCap(LineCapStyle.Round) // 圆角
            .padding({ left: 10, right: 10 })
            .offset({ x: `${this.tabIndex * this.tabIndicatorWidth}px`, y: 0 }) // 改变偏移量
            .animation({ duration: 300 })// 属性动画
        }.width('100%')
      }.backgroundColor(Color.White)
      Divider().color('#e8e8e8')
    }
  }
}

1.3 数据列表

根据数据的不同,展示的item的布局样式也不同,分为两种情况:单张图片和多张图片,下拉刷新和加载更多功能看我之前的写的帖子。

(部分代码)

......
  List() {
    ForEach(this.listNews, (item: NewsData) => {
      ListItem() {
        Column(){
          // 根据数据,展示不同的布局样式  
          if (item.thumbnail_pic_s02 == undefined) {
            // 单张图片样式  
            this.ItemSinglePic(item)
          } else {
            // 多张图片样式              
            this.ItemMorePic(item)
          }
        }.width('100%')
      }.padding(10)
    }, item => item.uniquekey)
  }
  .divider({ strokeWidth: 1, color: '#f5f5f5' })
......

2 侧边栏

侧边栏没有加入手势控制,只是简单的点击头像动画打开、点击阴影部分动画关闭,默认关闭状态
#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区
从以下代码看下,只需要设置值,设置属性动画之后,侧边栏动画效果就出来了,也是很方便的。

@Entry
@Component
struct MainPage {
  // 屏幕宽度
  private screenWidth = 0
  // 侧边栏的x位置
  @State sideBarX: number = -2000
  // 侧边栏背景的透明度
  @State sideBarBgopacity: number = 0
  // 侧边栏背景显示值
  @State sideBarBgVisibility: Visibility = Visibility.Hidden

  private aboutToAppear() {
    display.getDefaultDisplay((err, data) => {
      if (!err) {
        // 获取屏幕宽度  
        this.screenWidth = data.width
        // 设置侧边栏偏移量:负屏幕宽度  
        this.sideBarX = -this.screenWidth
      }
    })
  }

  // 打开侧边栏  
  private openSideMenu() {
    this.sideBarX = 0
    this.sideBarBgopacity = 1
    this.sideBarBgVisibility = Visibility.Visible
  }

  // 关闭侧边栏  
  private closeSideMenu() {
    this.sideBarX = -this.screenWidth
    this.sideBarBgopacity = 0
  }

  build() {
    Stack() {
      Column() {
        // 主页界面
      }

      // 半透明背景
      Stack()
        .width('100%')
        .height('100%')
        .backgroundColor('#80000000')
        .opacity(this.sideBarBgopacity)
        .animation({ // 属性动画,当透明度为0,隐藏背景
          duration: 300,
          onFinish:()=>{
            if(this.sideBarBgopacity == 0){
              this.sideBarBgVisibility = Visibility.Hidden
            }
          }
        })
        .visibility(this.sideBarBgVisibility)

      // 侧边栏
      Row() {
        Column() {
          SideMenu({ isLogin: $isLogin, closeMenu: () => {
            this.closeSideMenu()// 侧边栏布局
          } })
        }
        .width('70%')
        .height('100%')
        .backgroundColor(Color.White)

        Blank().onClick(() => {
          this.closeSideMenu()
        })
      }
      .width('100%')
      .height('100%')
      .position({ x: `${this.sideBarX}px`, y: 0 })// 动态改变位置
      .animation({ duration: 300 })// 属性动画
    }
    .width('100%')
    .height('100%')
  }
}

3 登录

登录也比较简单,只不过目前官网没有输入框的文档资料,这个输入框还是我从Codelabs:流式布局(eTS) 上面看到的。根据输入框是否有内容判断按钮的启用状态。

#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区 #星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区

虽然粘贴到编辑器中代码提示有错,但是可以正常运行和预览。密码框的类型还是我猜的!哈哈,就猜对了。

#星光计划2.0# HarmonyOS 项目实战之新闻头条(ArkUI-TS)-鸿蒙开发者社区

4 保存登录状态

根据官网资料:轻量级存储、官网示例还是有问题,我是问了华为的小伙伴,他给我说这个路径需要在/data/data/,但是目前模拟器对这块功能还兼容的不完善,不能持久化,如果把程序后台杀死,数据就没了

import dataStorage from '@ohos.data.storage';
// 设置存储的路径,路径必须在/data/data/下
const STORAGE_PATH = '/data/data/info'

export class InfoStorage {
  // 保存用户ID  
  setUserId(userId: string) {
    let store = dataStorage.getStorageSync(STORAGE_PATH)
    store.putSync('userId', userId)
  }

  // 获取用户ID  
  getUserId() {
    let store = dataStorage.getStorageSync(STORAGE_PATH)
    return store.getSync('userId', '').toString()
  }

}

总结

每天进步一点点、需要付出努力亿点点。

项目地址

https://gitee.com/liangdidi/NewsDemo.git(需要登录才能看到演示图)

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-12-15 09:12:30修改
8
收藏 7
回复
举报
2条回复
按时间正序
/
按时间倒序
深开鸿
深开鸿

一整套的ArkUI学习

 

回复
2021-12-18 22:40:00
起名字困难户
起名字困难户

楼主,ets的后端逻辑是直接在ets中编写的吗,还是通过调用PA实现逻辑交互
 

回复
2022-3-13 17:46:52
回复
    相关推荐