本來(lái)計(jì)劃要做一個(gè)本地?cái)?shù)據(jù)庫(kù)存儲(chǔ)相關(guān)的項(xiàng)目,但是官方的小伙伴給我說(shuō),ArkUI 數(shù)據(jù)庫(kù)目前不支持最新的模擬器,所以只能另想其他的。
想了大概一周,期間也在調(diào)研技術(shù)能否實(shí)現(xiàn),最終定了這個(gè)項(xiàng)目,真正的敲代碼開(kāi)發(fā)大概就用了一天,最難的就是想法和界面設(shè)計(jì)。
不得不感慨新框架真的方便、真的好用!期待下個(gè)版本能完善組件和 API 的細(xì)節(jié)。也希望 HarmonyOS 越來(lái)越好。
界面搭建基于 ArkUI 中 TS 擴(kuò)展的聲明式開(kāi)發(fā)范式,關(guān)于語(yǔ)法和概念直接看官網(wǎng)官方文檔地址。
基于 TS 擴(kuò)展的聲明式開(kāi)發(fā)范式 1:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-ts-overview-0000001192705715基于 TS 擴(kuò)展的聲明式開(kāi)發(fā)范式 2:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-framework-directory-0000001111581264
項(xiàng)目說(shuō)明
使用系統(tǒng)自帶的網(wǎng)絡(luò)請(qǐng)求框架,根據(jù) Tab 的類(lèi)型切換請(qǐng)求對(duì)應(yīng)的數(shù)據(jù)。列表支持下拉刷新、上拉加載更多。
模擬登錄效果,根據(jù)輸入框來(lái)確定按鈕是否啟用,登錄成功后,顯示登錄頭像和用戶(hù)信息。
①數(shù)據(jù)請(qǐng)求:聚合免費(fèi) API-新聞?lì)^條
https://www.juhe.cn/docs/api/id/235②網(wǎng)絡(luò)請(qǐng)求:1-官方文檔、2-ArkUI 開(kāi)發(fā)基礎(chǔ):網(wǎng)絡(luò)請(qǐng)求
https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-net-http-0000001168304341 https://developer.huawei.com/consumer/cn/forum/topic/0203723866728880326?fid=0101591351254000314③列表刷新:ArkUI(TS)聲明式開(kāi)發(fā):列表下拉刷新、上拉加載更多
https://developer.huawei.com/consumer/cn/forum/topic/0203729154661360497?fid=0101591351254000314
效果演示
在遠(yuǎn)程模擬器錄制的,效果不佳:
功能解析
①首頁(yè)
首頁(yè)從上至下分為三個(gè)部分:
標(biāo)題欄
Tab 標(biāo)簽
數(shù)據(jù)列表
標(biāo)題欄:布局很簡(jiǎn)單,使用 Row 布局包裹:Image 和 Swiper(搜索框中文字上下切換)。
部分代碼:
...... //標(biāo)題欄 @BuilderCustomTitleBar(){ 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(){ //搜索圖標(biāo) 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)//自動(dòng)播放 .indicator(false)//隱藏指示器 .interval(3000)//切換間隔時(shí)間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}) } ......
Tab 標(biāo)簽:這個(gè)也比較簡(jiǎn)單,根據(jù)屏幕寬度、tab 標(biāo)簽的總數(shù)量,就能得出 tabItem 的寬度。
底部設(shè)置的指示器,點(diǎn)擊 tab 根據(jù) index(當(dāng)前索引) * itemWithd(每個(gè) tab 的寬度)設(shè)置屬性動(dòng)畫(huà),切換效果就可以了。
import{TabModel,getTabList}from'../../model/tabModel.ets'; importdisplayfrom'@ohos.display'; @Component exportstructHomeTabs{ //Tab數(shù)據(jù) privatelistTab=getTabList() //tabItem平均寬度 @StatetabIndicatorWidth:number=152 //指示器 @StatetabIndex:number=0 //對(duì)外暴露的方法 privatetabClick:(item:TabModel)=>void privateaboutToAppear(){ display.getDefaultDisplay((err,data)=>{ if(!err){ //獲取tabItem平均寬度 this.tabIndicatorWidth=data.width/this.listTab.length } }) } build(){ Column(){ Stack({alignContent:Alignment.Bottom}){ //tab內(nèi)容 Row(){ ForEach(this.listTab,item=>{ Button(){ Text(item.name) .fontSize(this.tabIndex==item.id?15:13)//根據(jù)當(dāng)前選中改變字體大小 .fontColor(this.tabIndex==item.id?$r('app.color.app_theme'):'#000000')//根據(jù)當(dāng)前選中改變字體顏色 } .layoutWeight(1) .height(35) .type(ButtonType.Normal) .backgroundColor(Color.White) .onClick(()=>{ this.tabIndex=item.id//更新索引 this.tabClick(item)//提供給外部調(diào)用 }) },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})//屬性動(dòng)畫(huà) }.width('100%') }.backgroundColor(Color.White) Divider().color('#e8e8e8') } } }
數(shù)據(jù)列表:根據(jù)數(shù)據(jù)的不同,展示的 item 的布局樣式也不同,分為兩種情況:?jiǎn)螐垐D片和多張圖片,下拉刷新和加載更多功能看我之前的寫(xiě)的帖子。
部分代碼:
...... List(){ ForEach(this.listNews,(item:NewsData)=>{ ListItem(){ Column(){ //根據(jù)數(shù)據(jù),展示不同的布局樣式 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'}) ......
②側(cè)邊欄
側(cè)邊欄沒(méi)有加入手勢(shì)控制,只是簡(jiǎn)單的點(diǎn)擊頭像動(dòng)畫(huà)打開(kāi)、點(diǎn)擊陰影部分動(dòng)畫(huà)關(guān)閉,默認(rèn)關(guān)閉狀態(tài)。
從以下代碼看下,只需要設(shè)置值,設(shè)置屬性動(dòng)畫(huà)之后,側(cè)邊欄動(dòng)畫(huà)效果就出來(lái)了,也是很方便的。
@Entry @Component structMainPage{ //屏幕寬度 privatescreenWidth=0 //側(cè)邊欄的x位置 @StatesideBarX:number=-2000 //側(cè)邊欄背景的透明度 @StatesideBarBgopacity:number=0 //側(cè)邊欄背景顯示值 @StatesideBarBgVisibility:Visibility=Visibility.Hidden privateaboutToAppear(){ display.getDefaultDisplay((err,data)=>{ if(!err){ //獲取屏幕寬度 this.screenWidth=data.width //設(shè)置側(cè)邊欄偏移量:負(fù)屏幕寬度 this.sideBarX=-this.screenWidth } }) } //打開(kāi)側(cè)邊欄 privateopenSideMenu(){ this.sideBarX=0 this.sideBarBgopacity=1 this.sideBarBgVisibility=Visibility.Visible } //關(guān)閉側(cè)邊欄 privatecloseSideMenu(){ this.sideBarX=-this.screenWidth this.sideBarBgopacity=0 } build(){ Stack(){ Column(){ //主頁(yè)界面 } //半透明背景 Stack() .width('100%') .height('100%') .backgroundColor('#80000000') .opacity(this.sideBarBgopacity) .animation({//屬性動(dòng)畫(huà),當(dāng)透明度為0,隱藏背景 duration:300, onFinish:()=>{ if(this.sideBarBgopacity==0){ this.sideBarBgVisibility=Visibility.Hidden } } }) .visibility(this.sideBarBgVisibility) //側(cè)邊欄 Row(){ Column(){ SideMenu({isLogin:$isLogin,closeMenu:()=>{ this.closeSideMenu()//側(cè)邊欄布局 }}) } .width('70%') .height('100%') .backgroundColor(Color.White) Blank().onClick(()=>{ this.closeSideMenu() }) } .width('100%') .height('100%') .position({x:`${this.sideBarX}px`,y:0})//動(dòng)態(tài)改變位置 .animation({duration:300})//屬性動(dòng)畫(huà) } .width('100%') .height('100%') } }
③登錄
登錄也比較簡(jiǎn)單,只不過(guò)目前官網(wǎng)沒(méi)有輸入框的文檔資料,這個(gè)輸入框還是我從Codelabs:流式布局(eTS)上面看到的。根據(jù)輸入框是否有內(nèi)容判斷按鈕的啟用狀態(tài)。
雖然粘貼到編輯器中代碼提示有錯(cuò),但是可以正常運(yùn)行和預(yù)覽。密碼框的類(lèi)型還是我猜的!哈哈,就猜對(duì)了。
④保存登錄狀態(tài)
根據(jù)官網(wǎng)資料:輕量級(jí)存儲(chǔ)、官網(wǎng)示例還是有問(wèn)題。
https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-data-storage-0000001117163542
我是問(wèn)了華為的小伙伴,他給我說(shuō)這個(gè)路徑需要在 /data/data/,但是目前模擬器對(duì)這塊功能還兼容的不完善,不能持久化,如果把程序后臺(tái)殺死,數(shù)據(jù)就沒(méi)了。
importdataStoragefrom'@ohos.data.storage'; //設(shè)置存儲(chǔ)的路徑,路徑必須在/data/data/下 constSTORAGE_PATH='/data/data/info' exportclassInfoStorage{ //保存用戶(hù)ID setUserId(userId:string){ letstore=dataStorage.getStorageSync(STORAGE_PATH) store.putSync('userId',userId) } //獲取用戶(hù)ID getUserId(){ letstore=dataStorage.getStorageSync(STORAGE_PATH) returnstore.getSync('userId','').toString() } }
項(xiàng)目地址如下(需要登錄才能看到演示圖):
https://gitee.com/liangdidi/NewsDemo.git
作者:梁青松
原文標(biāo)題:HarmonyOS版“新聞?lì)^條”APP開(kāi)發(fā)實(shí)戰(zhàn)
文章出處:【微信公眾號(hào):HarmonyOS技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
7文章
3765瀏覽量
64276 -
代碼
+關(guān)注
關(guān)注
30文章
4748瀏覽量
68355 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1967瀏覽量
30018
原文標(biāo)題:HarmonyOS版“新聞?lì)^條”APP開(kāi)發(fā)實(shí)戰(zhàn)
文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論