# HarmonyOS-ArkUI-NewsAPP **Repository Path**: blackteaYJ/harmony-os-ark-ui-news-app ## Basic Information - **Project Name**: HarmonyOS-ArkUI-NewsAPP - **Description**: HarmonyOS ArkUI(ets):仿“腾讯新闻”APP - **Primary Language**: TypeScript - **License**: Not specified - **Default Branch**: master - **Homepage**: https://developer.huawei.com/consumer/cn/forum/topic/0201751703047580635?fid=0102683795438680754 - **GVP Project**: No ## Statistics - **Stars**: 19 - **Forks**: 3 - **Created**: 2021-12-19 - **Last Updated**: 2025-09-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### ArkUI(ets)仿“腾讯新闻”APP 之前都是用模拟数据做的app,还没做过网络api方面的练习,所以就找免费的api,适合的免费api真的挺少。聚合、进制、京东等免费的api基本只有新闻列表,要么就是数据很乱或者是查星座、笑话之类的,只有聚合有新闻、视频、新闻详情的接口,但视频的接口video组件用不了~~(起码还有能用的免费api,感谢)。 api定了,那就做新闻类的app吧。 聚合用了新闻列表和新闻详情接口,姬长信用了个视频接口(只有少量用于测试的数据,原本是可以在线播视频的,用了两次之后发现播不了~~) [聚合API](https://www.juhe.cn/) 用聚合要先注册自己的账号,申请API,就会获取当前API的key(私人key,不可更改,每天免费50次请求,我注册的时候还是100次的,wuwuwu) [姬长信API](https://api.isoyu.com/#/) ## 一、效果演示 1、新闻列表页 | 2、新闻详情页、图片展示页 :-|:- ![新闻列表](/gif素材/P40_新闻1.gif) | ![新闻列表](/gif素材/P40_新闻2.gif) 3、视频页 | 4、动态页 ![新闻列表](/gif素材/P40_视频.gif) | ![新闻列表](/gif素材/P40_动态.gif) ## 二、 流程图 --本来自定义了视频的控制栏的,但是发现VideoController()控制器的bug会导致控制器失效,所以没继续做。视频页先不搞了。 ![新闻列表](/gif素材/仿腾讯新闻.png) ## 三、文件组织(“我的页面”没做,视频页面因为bug没做完) ![新闻列表](/gif素材/文件组织.png) ## 四、思路与实现 ### 1、网络连接 我是做成一个POST通用请求,有点粗糙(也没做错误处理)不要介意,能用就行,哈哈 [数据请求官方文档链接](https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-net-http-0000001168304341) ``` /* * 通用请求 * url:链接 * params:参数 */ export function DataHelperPOST(url: string, params: Object) { let httpRequest = http.createHttp() let promise = httpRequest.request(url, { method: http.POST, extraData: params, connectTimeout: 30000, readTimeout: 30000, header: { 'Content-Type': 'application/json' } }) return promise.then((value) => { let data:{ code: number, data: any } = { code: 0, data: '' } data.code = value.responseCode if(value.responseCode === 200) { data.data = JSON.parse(JSON.stringify(value.result)); } return data }) } //调用 export async function GetNewsList(newsType: string) { //接口链接 let newsUrl: string = `http://v.juhe.cn/toutiao/index`; //参数 let params: Object = { "type": newsType, "key": NewsKey } let getData = await DataHelperPOST(newsUrl, params); } ``` ### 2、自定义固定Tabs菜单,点击菜单切换,滑动页面菜单跟随 菜单用Scroll包裹设置为横向滑动,用display.getDefaultDisplay() 获取屏幕宽度(预览器无法获取),根据每个tab的固定宽度算出屏幕能显示多少个tab,用this.scroller.currentOffset().xOffset获取滑动偏移值,依据当前的index与偏移值的关系设置Scroll的偏移值this.scroller.scrollTo() [获取屏幕大小接口官方文档链接](https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-display-0000001177660291) ![新闻列表](/gif素材/tabs菜单.gif) ### 3、下拉刷新 ##### 主要用List,List下拉自带弹簧效果 1、内容item用ForEach循环,再在最前面加一个刷新 item 10%高度,整个List往上偏移10%,List高度为110%,用parallelGesture绑定滑动事件。 2、往下滑动时未达到刷新行程,释放自动回弹(List自带的弹簧效果)。达到刷新行程,释放,List整体往下偏移10%(固定显示刷新item),刷新完成,List往上偏移10%(隐藏刷新 item)。 ``` List() { //刷新 item ListItem() { Column() { Image($r("app.media.jiazaizhong_1")) .objectFit(ImageFit.Contain) .height('50%').aspectRatio(1) .margin({ bottom: 5 }) .rotate({ x: 0, y: 0, z: 1, angle: this.rotateAngle }) Text(this.loadingText) .fontSize(14) .fontColor($r("app.color.fontColor_text2")) } } .width('100%').height(`${this.loadingHeight}%`) ForEach(this.tabData, item => { ListItem() { //内容item } }, item => item.id) } .position({ x: 0, y: `-${this.loadingYOffset}%` }) .width('100%').height(`${100 + this.loadingYOffset}%`) ``` ### 4、新闻内容详情页 api获取的内容数据是html字符串,直接显示的话只能显示一堆很乱的文本,所以要将html字符串处理一下,把里面的文本、图片提取出来。 可以用charAt()逐字循环、indexOf()循环处理文本,把处理好的文本、图片存到一个数组里[{"node": "text", "content": "文本····"}, {"node": "img", "content": "图片地址"}],在显示页面用for循环判断node,显示Text或Image (因为聚合的这个新闻内容只有图片和文字,而且格式也很整齐,所以处理起来比较简单。如果是整个页面的话也可以,但处理文本就比较复杂。而且不同网站的结构也不一样,如果玩过爬取的话这个就很简单了) ![新闻列表](/gif素材/文本.png) ![新闻列表](/gif素材/P40_新闻3.gif) ### 5、自定义视频控制栏 #### ①Stack #### ②position 用Stack或position在视频前面设置一个控制栏,再用VideoController()控制器控制视频暂停/播放,用滑动条Slider()设置为进度条,用Video的onUpdate()方法实时更新进度条,用Slider()的onChange()事件设置拉动进度条播放。 ``` Stack(){ Video() Column() { //自定义控制栏 } } Column() { Video() Column() { //自定义控制栏 } .position({x: 0, y: 0}) //透明度渐变 //控制栏顶部(标题)与底部(进度条)黑色半透明向中间全透明渐变 .linearGradient({colors: [['#a6000000', 0.0],['#00000000', 0.2], ['#00000000', 0.8], ['#a6000000', 1.0]]}) } ``` ## 五、多机型适配(API7只有P40 Pro能开) MatePadPro ![新闻列表](/gif素材/MatePadPro.gif) MateX2 ![新闻列表](/gif素材/MateX2.gif) ## 六、代码 下载代码的同学记得要填上自己的聚合API key,才能请求数据:data/NewsData ![新闻列表](/gif素材/接口key.png) 如果要用模拟器请求数据要把这两个注释去掉:common/TabsTypePage:aboutToAppear()方法下面 (还有index主页的获取屏幕Size的方法,预览器无法设置/获取,还会报错,有点烦。所以注释掉) ![新闻列表](/gif素材/缓存和刷新.png) 完。继续学习,共同进步。谢谢!