• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

鸿蒙开发新闻头条

武飞扬头像
鸿蒙开发者老王
帮助2

大家好,我是老王~
 

本来计划要做一个本地数据库存储相关的项目,但是官方的小伙伴给我说,ArkUI数据库目前不支持最新的模拟器。

所以只能另想其他的,想了大概一周,期间也在调研技术能否实现,纠结的很,想着要不断的突破自己,至少比上一次的要好!最终定了这个项目,真正的敲代码开发可能就用了一天,最难的就是想法和界面设计。

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

一、项目说

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

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-ts-overview-0000001192705715

使用系统自带的网络请求框架,根据Tab的类型切换请求对应的数据。列表支持下拉刷新、上拉加载更多。

模拟登录效果,根据输入框来确定按钮是否启用,登录成功后,显示登录头像和用户信息。

  • 数据请求:聚合免费API-新闻头条

  • 网络请求:1-官方文档、2-ArkUI开发基础:网络请求

  • 列表刷新:ArkUI(TS)声明式开发:列表下拉刷新、上拉加载更多

二、效果演示

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

学新通

             

学新通

三、功能解析

1、主页

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

  1. 标题栏、

  2. Tab标签、

  3. 数据列表

学新通

  • 标题栏

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

(部分代码)

  1.  
    ......
  2.  
    // 标题栏
  3.  
    @Builder CustomTitleBar() {
  4.  
    Row() {
  5.  
    // 头像
  6.  
    Image(this.isLogin ? $r('app.media.ic_ldd_headpic') : $r('app.media.ic_default_headpic'))
  7.  
    .width(30)
  8.  
    .height(30)
  9.  
    .borderRadius(15)
  10.  
    .margin({ right: 10 })
  11.  
    .onClick(() => {
  12.  
    this.openSideMenu()
  13.  
    })
  14.  
     
  15.  
    // 搜索框
  16.  
    Row() {
  17.  
    // 搜索图标
  18.  
    Image($r('app.media.ic_search'))
  19.  
    .width(15).height(15)
  20.  
    .margin({ left: 10 })
  21.  
     
  22.  
    // 视图上下切换
  23.  
    Swiper() {
  24.  
    ForEach(this.listSearch, item => {
  25.  
    Text(item)
  26.  
    .height('100%')
  27.  
    .fontSize(12)
  28.  
    .fontColor('#505050')
  29.  
    .margin({ left: 10 })
  30.  
    }, item => item)
  31.  
    }
  32.  
    .vertical(true) // 方向:纵向
  33.  
    .autoPlay(true) // 自动播放
  34.  
    .indicator(false) // 隐藏指示器
  35.  
    .interval(3000) // 切换间隔时间3
  36.  
    }
  37.  
    .layoutWeight(1)
  38.  
    .height('100%')
  39.  
    .backgroundColor('#F1F1F1')
  40.  
    .borderRadius(15)
  41.  
    }
  42.  
    .width('100%')
  43.  
    .height(50)
  44.  
    .backgroundColor(Color.White)
  45.  
    .padding({ top: 10, bottom: 10, left: 15, right: 15 })
  46.  
    }
  47.  
    ......
学新通
  • Tab标签

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

  1.  
    import { TabModel,getTabList} from '../../model/tabModel.ets';
  2.  
    import display from '@ohos.display';
  3.  
    @Component
  4.  
    export struct HomeTabs {
  5.  
    // Tab数据
  6.  
    private listTab = getTabList()
  7.  
    // tabItem平均宽度
  8.  
    @State tabIndicatorWidth: number = 152
  9.  
    // 指示器
  10.  
    @State tabIndex: number = 0
  11.  
    // 对外暴露的方法
  12.  
    private tabClick: (item: TabModel) => void
  13.  
     
  14.  
    private aboutToAppear() {
  15.  
    display.getDefaultDisplay((err, data) => {
  16.  
    if (!err) {
  17.  
    // 获取tabItem平均宽度
  18.  
    this.tabIndicatorWidth = data.width / this.listTab.length
  19.  
    }
  20.  
    })
  21.  
    }
  22.  
     
  23.  
    build() {
  24.  
    Column(){
  25.  
    Stack({ alignContent: Alignment.Bottom }) {
  26.  
    // tab内容
  27.  
    Row() {
  28.  
    ForEach(this.listTab, item => {
  29.  
    Button() {
  30.  
    Text(item.name)
  31.  
    .fontSize(this.tabIndex == item.id ? 15 : 13) // 根据当前选中改变字体大小
  32.  
    .fontColor(this.tabIndex == item.id ? $r('app.color.app_theme') : '#000000')// 根据当前选中改变字体颜色
  33.  
    }
  34.  
    .layoutWeight(1)
  35.  
    .height(35)
  36.  
    .type(ButtonType.Normal)
  37.  
    .backgroundColor(Color.White)
  38.  
    .onClick(() => {
  39.  
    this.tabIndex = item.id // 更新索引
  40.  
    this.tabClick(item) // 提供给外部调用
  41.  
    })
  42.  
    }, item => item.tabType)
  43.  
    }.height(35)
  44.  
     
  45.  
    // 指示器
  46.  
    Row() {
  47.  
    Divider()
  48.  
    .width(`${this.tabIndicatorWidth}px`) // 平均宽度
  49.  
    .strokeWidth(3)
  50.  
    .color($r('app.color.app_theme'))
  51.  
    .lineCap(LineCapStyle.Round) // 圆角
  52.  
    .padding({ left: 10, right: 10 })
  53.  
    .offset({ x: `${this.tabIndex * this.tabIndicatorWidth}px`, y: 0 }) // 改变偏移量
  54.  
    .animation({ duration: 300 })// 属性动画
  55.  
    }.width('100%')
  56.  
    }.backgroundColor(Color.White)
  57.  
    Divider().color('#e8e8e8')
  58.  
    }
  59.  
    }
  60.  
    }
学新通
  • 数据列表

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

(部分代码)

  1.  
    ......
  2.  
    List() {
  3.  
    ForEach(this.listNews, (item: NewsData) => {
  4.  
    ListItem() {
  5.  
    Column(){
  6.  
    // 根据数据,展示不同的布局样式
  7.  
    if (item.thumbnail_pic_s02 == undefined) {
  8.  
    // 单张图片样式
  9.  
    this.ItemSinglePic(item)
  10.  
    } else {
  11.  
    // 多张图片样式
  12.  
    this.ItemMorePic(item)
  13.  
    }
  14.  
    }.width('100%')
  15.  
    }.padding(10)
  16.  
    }, item => item.uniquekey)
  17.  
    }
  18.  
    .divider({ strokeWidth: 1, color: '#f5f5f5' })
  19.  
    ......
学新通

2、侧边栏

侧边栏没有加入手势控制,只是简单的点击头像动画打开、点击阴影部分动画关闭,默认关闭状态

学新通

  1.  
    @Entry
  2.  
    @Component
  3.  
    struct MainPage {
  4.  
    // 屏幕宽度
  5.  
    private screenWidth = 0
  6.  
    // 侧边栏的x位置
  7.  
    @State sideBarX: number = -2000
  8.  
    // 侧边栏背景的透明度
  9.  
    @State sideBarBgopacity: number = 0
  10.  
    // 侧边栏背景显示值
  11.  
    @State sideBarBgVisibility: Visibility = Visibility.Hidden
  12.  
     
  13.  
    private aboutToAppear() {
  14.  
    display.getDefaultDisplay((err, data) => {
  15.  
    if (!err) {
  16.  
    // 获取屏幕宽度
  17.  
    this.screenWidth = data.width
  18.  
    // 设置侧边栏偏移量:负屏幕宽度
  19.  
    this.sideBarX = -this.screenWidth
  20.  
    }
  21.  
    })
  22.  
    }
  23.  
     
  24.  
    // 打开侧边栏
  25.  
    private openSideMenu() {
  26.  
    this.sideBarX = 0
  27.  
    this.sideBarBgopacity = 1
  28.  
    this.sideBarBgVisibility = Visibility.Visible
  29.  
    }
  30.  
     
  31.  
    // 关闭侧边栏
  32.  
    private closeSideMenu() {
  33.  
    this.sideBarX = -this.screenWidth
  34.  
    this.sideBarBgopacity = 0
  35.  
    }
  36.  
     
  37.  
    build() {
  38.  
    Stack() {
  39.  
    Column() {
  40.  
    // 主页界面
  41.  
    }
  42.  
     
  43.  
    // 半透明背景
  44.  
    Stack()
  45.  
    .width('100%')
  46.  
    .height('100%')
  47.  
    .backgroundColor('#80000000')
  48.  
    .opacity(this.sideBarBgopacity)
  49.  
    .animation({ // 属性动画,当透明度为0,隐藏背景
  50.  
    duration: 300,
  51.  
    onFinish:()=>{
  52.  
    if(this.sideBarBgopacity == 0){
  53.  
    this.sideBarBgVisibility = Visibility.Hidden
  54.  
    }
  55.  
    }
  56.  
    })
  57.  
    .visibility(this.sideBarBgVisibility)
  58.  
     
  59.  
    // 侧边栏
  60.  
    Row() {
  61.  
    Column() {
  62.  
    SideMenu({ isLogin: $isLogin, closeMenu: () => {
  63.  
    this.closeSideMenu()// 侧边栏布局
  64.  
    } })
  65.  
    }
  66.  
    .width('70%')
  67.  
    .height('100%')
  68.  
    .backgroundColor(Color.White)
  69.  
     
  70.  
    Blank().onClick(() => {
  71.  
    this.closeSideMenu()
  72.  
    })
  73.  
    }
  74.  
    .width('100%')
  75.  
    .height('100%')
  76.  
    .position({ x: `${this.sideBarX}px`, y: 0 })// 动态改变位置
  77.  
    .animation({ duration: 300 })// 属性动画
  78.  
    }
  79.  
    .width('100%')
  80.  
    .height('100%')
  81.  
    }
  82.  
    }
学新通

从以上代码看,只需要设置值,设置属性动画之后,侧边栏动画效果就出来了,也是很方便的。

3、登录

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

学新通

学新通

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

学新通

4、保存登录状态

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

  1.  
    import dataStorage from '@ohos.data.storage';
  2.  
    // 设置存储的路径,路径必须在/data/data/
  3.  
    const STORAGE_PATH = '/data/data/info'
  4.  
     
  5.  
    export class InfoStorage {
  6.  
    // 保存用户ID
  7.  
    setUserId(userId: string) {
  8.  
    let store = dataStorage.getStorageSync(STORAGE_PATH)
  9.  
    store.putSync('userId', userId)
  10.  
    }
  11.  
     
  12.  
    // 获取用户ID
  13.  
    getUserId() {
  14.  
    let store = dataStorage.getStorageSync(STORAGE_PATH)
  15.  
    return store.getSync('userId', '').toString()
  16.  
    }
  17.  
     
  18.  
    }
学新通

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

——————

原创:老王丨【公众号:鸿蒙开发者老王】华为认证讲师 / 腾讯认证讲师 / 鸿蒙开发先行者

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhibakif
系列文章
更多 icon
同类精品
更多 icon
继续加载