# NewsDemo **Repository Path**: dot_happydz_admin/NewsDemo ## Basic Information - **Project Name**: NewsDemo - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-12-09 - **Last Updated**: 2021-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ArkUI(TS)声明式项目实战开发:新闻头条 ### 前言 本来计划要做一个本地数据库存储相关的项目,但是官方的小伙伴给我说,**ArkUI数据库目前不支持最新的模拟器**。所以只能另想其他的,想了大概一周,期间也在调研技术能否实现,纠结的很,想着要不断的突破自己,至少比上一次的要好!最终定了这个项目,真正的敲代码开发可能就用了一天,最难的就是想法和界面设计。 不得不感慨新框架真的方便、真的好用!期待下个版本能完善组件和API的细节。也希望鸿蒙越来越好。。。。。 ### 项目说明 界面搭建基于ArkUI中TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:[基于TS扩展的声明式开发范式1](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-ts-overview-0000001192705715)、[基于TS扩展的声明式开发范式2](https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-framework-directory-0000001111581264) 使用系统自带的网络请求框架,根据Tab的类型切换请求对应的数据。列表支持下拉刷新、上拉加载更多。模拟登录效果,根据输入框来确定按钮是否启用,登录成功后,显示登录头像和用户信息。 - 数据请求:[聚合免费API-新闻头条](https://www.juhe.cn/docs/api/id/235) - 网络请求:[1-官方文档](https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-net-http-0000001168304341)、[2-ArkUI开发基础:网络请求](https://developer.huawei.com/consumer/cn/forum/topic/0203723866728880326?fid=0101591351254000314) - 列表刷新:[ArkUI(TS)声明式开发:列表下拉刷新、上拉加载更多](https://developer.huawei.com/consumer/cn/forum/topic/0203729154661360497?fid=0101591351254000314) ### 效果演示 在远程模拟器录制的,效果不佳 ~_~ ![](image/demo.gif) ![](image/demo1.gif) ### 功能解析 #### 1、主页 主页从上至下分为三个部分:1.标题栏、2.Tab标签、3.数据列表 ![](image/demo2.png) ##### 1.标题栏 布局很简单,使用Row布局包裹:Image 和 Swiper(搜索框中文字上下切换) **(部分代码)** ```typescript ...... // 标题栏 @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 }) } ...... ``` ##### 2.Tab标签 这个也比较简单,根据屏幕宽度、tab标签的总数量,就能得出tabItem的宽度。底部设置的指示器,点击tab根据 index(当前索引) * itemWithd(每个tab的宽度)设置属性动画,切换效果就可以了。 ```typescript 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') } } } ``` ##### 3.数据列表 根据数据的不同,展示的item的布局样式也不同,分为两种情况:单张图片和多张图片,下拉刷新和加载更多功能看我之前的写的帖子。 **(部分代码)** ```typescript ...... 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、侧边栏 侧边栏没有加入手势控制,只是简单的点击头像动画打开、点击阴影部分动画关闭,默认关闭状态 ![](image/demo3.png) 从以下代码看下,只需要设置值,设置属性动画之后,侧边栏动画效果就出来了,也是很方便的。 ```typescript @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)](https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/HarmonyOS-FlowLayout) 上面看到的。根据输入框是否有内容判断按钮的启用状态。 ![](image/demo4.png) ![](image/demo5.png) 虽然粘贴到编辑器中代码提示有错,但是可以正常运行和预览。密码框的类型还是我猜的!哈哈,就猜对了。 ![](image/demo7.png) #### 4、保存登录状态 根据官网资料:[轻量级存储](https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-data-storage-0000001117163542)、官网示例还是有问题,我是问了华为的小伙伴,他给我说这个路径需要在/data/data/,但是目前模拟器对这块功能还兼容的不完善,**不能持久化,如果把程序后台杀死,数据就没了** ```typescript 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](https://gitee.com/liangdidi/NewsDemo.git)(需要登录才能看到演示图) ### **结尾** 每天进步一点点、需要付出努力亿点点。