前言:
之前写过一个demo 但是那个是本地数据 现在我改成网络的 接口是我本地服务的 就想着分享给大家
作者:徐庆
团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合作。
效果图:
具体实现:
添加权限
启动本地服务
拿到项目里面的bosstabservice 配置好数据库 启动服务
客户端实现;
-
1 底部导航器实现
import CompanylistComponent from '../view/CompanylistComponent';
import MessageListComponent from '../view/MessageListComponent';
import My from '../view/My';
import PositionListComponent from '../view/PositionListComponent';
@Entry
@Component
struct ListIndex {
private controller: TabsController = new TabsController()
@State SelectPos:number=0;
public IndexClick(){
this.SelectPos=0;
this.controller.changeIndex(0)
}
public messageClick(){
this.SelectPos=1;
this.controller.changeIndex(1)
}
public myClick(){
this.SelectPos=2;
this.controller.changeIndex(2)
}
public meClick(){
this.SelectPos=3;
this.controller.changeIndex(3)
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Tabs({ controller: this.controller }) {
TabContent() {
PositionListComponent()
}
TabContent() {
CompanylistComponent()
}
TabContent() {
MessageListComponent()
}
TabContent() {
My()
}
}
.scrollable(false)
.barHeight(0)
.animationDuration(0)
Row() {
Column(){
Image((this.SelectPos==0?$r('app.media.ic_main_tab_company_pre'):$r('app.media.ic_main_tab_company_nor')))
.width(30).height(30)
Text('职位')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==0?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.IndexClick.bind(this))
Column(){
Image((this.SelectPos==1?$r('app.media.ic_main_tab_find_pre'):$r('app.media.ic_main_tab_find_nor')))
.width(30).height(30)
Text('公司')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==1?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.messageClick.bind(this))
Column(){
Image((this.SelectPos==2?$r('app.media.ic_main_tab_contacts_pre'):$r('app.media.ic_main_tab_contacts_nor')))
.width(30).height(30)
Text('消息')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==2?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.myClick.bind(this))
Column(){
Image((this.SelectPos==3?$r('app.media.ic_main_tab_my_pre'):$r('app.media.ic_main_tab_my_nor')))
.width(30).height(30)
Text('我的')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==3?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.meClick.bind(this))
}.alignItems(VerticalAlign.Bottom).width('100%').height(65).margin({top:0,right:0,bottom:10,left:0})
}
.width('100%')
.height('100%')
}
}
底部导航使用 Row组件配合 Column 组件实现底导航器 然后配置 Tabs组件实现我们上门的碎片也和底部导航器点击切换效果
职位页面实现:
初始化生命接口 网络请求
async aboutToAppear(){
Logger.error(this.TAG+' aboutToAppear --- > ');
let networkurl=CommonConstant.DISH;
await httpRequestGet(networkurl).then((data)=>{
console.log("data --- > "+data);
Logger.error("登录请求回调结果 ---> " +data.toString());
let positionmodel : PositionModel = JSON.parse(data.toString())
this.JokeList = positionmodel.data
});
}
解析服务端响应数据创建数据model
export class PositionModel {
msg: string = ""
data: Array<Positiondata> = []
code:number=0
}
export class Positiondata {
id: string = ""
name: string = ""
cname: string = ""
size: string = ""
salary: string = ""
username: string = ""
title: string = ""
}
解析数据
let positionmodel : PositionModel = JSON.parse(data.toString())
this.JokeList = positionmodel.data
网络请求工具类
import http from '@ohos.net.http';
import Constants, { ContentType } from '../common/Constants';
import Logger from './Logger';
export function httpRequestGet(url: string) {
return httpRequest(url, http.RequestMethod.GET);
}
/*export function httpRequestGet(url: string, params?:string) {
return httpRequest(url, http.RequestMethod.GET,params);
}*/
export function httpRequestPost(url: string, params?:string ) {
return httpRequest(url, http.RequestMethod.POST, params);
}
function httpRequest(url: string, method: http.RequestMethod,params?: string){
let httpRequest = http.createHttp();
let responseResult = httpRequest.request(url, {
method: method,
readTimeout: Constants.HTTP_READ_TIMEOUT,//读取超时时间 可选,默认为60000ms
header: {
'Content-Type': ContentType.JSON
},
connectTimeout: Constants.HTTP_READ_TIMEOUT,//连接超时时间 可选,默认为60000ms
extraData: params // 请求参数
});
return responseResult.then((value: http.HttpResponse)=>{
Logger.error("请求状态 --> "+value.responseCode)
if(value.responseCode===200){
Logger.error("请求成功");
let getresult = value.result;
Logger.error('请求返回数据', JSON.stringify(getresult));
return getresult;
}
}).catch((err)=>{
httpRequest.off('headersReceive');
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
return "";
});
}
日志工具类
import hilog from '@ohos.hilog';
class Logger {
private domain: number;
private prefix: string;
private format: string = '%{public}s, %{public}s';
/**
* constructor.
*
* @param Prefix Identifies the log tag.
* @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
*/
constructor(prefix: string = 'MyApp', domain: number = 0xFF00) {
this.prefix = prefix;
this.domain = domain;
}
debug(...args: string[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
info(...args: string[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
warn(...args: string[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
error(...args: string[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export default new Logger('HTTPS', 0xFF00)
职位页面布局实现
build() {
Column(){
Row(){
Text('职位')
.fontSize(30)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
} .width('100%')
.height(50)
.backgroundColor(Color.Green)
.justifyContent(FlexAlign.Center)
List({ space: commonConst.LIST_ITEM_SPACE }) {
ForEach(this.JokeList , (item) => {
ListItem() {
Row() {
Column() {
Row() {
Text(item?.name).fontSize(14).fontColor($r('app.color.gray'))
Text(item?.salary).fontSize(20).fontColor($r('app.color.freshRed'))
.margin({left:220})
.align(Alignment.BottomStart)
}.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.GOODS_LIST_WIDTH)
Text(item?.cname)
.fontSize(25)
.margin({ left: 10 })
Divider().width('80%')
Text(item?.username)
.fontColor($r('app.color.greentext'))
.fontSize(12)
.margin({ left:10, top: 10 })
}
//.padding(commonConst.GOODS_LIST_PADDING)
.width(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
.height(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
.justifyContent(FlexAlign.Start)
}
.justifyContent(FlexAlign.Center)
.height(commonConst.GOODS_LIST_HEIGHT)
.width(commonConst.LAYOUT_WIDTH_OR_HEIGHT)
}
})
}
.width(commonConst.GOODS_LIST_WIDTH)
}.width('100%')
.height('100%')
}
}
其他的company 公司页面 和message 消息页面和职位页面实现方式类似 我这边就不展开讲了 都是进去页面第一个生命周期进行网络请求 然后解析数据赋值给数组然后渲染在我们的list组件上面
个人页面实现:
@Entry
@Component
export default struct Discover {
@State message: string = 'Hello World'
@Builder NavigationTitle() {
Row(){
Text("发现")
.width('100')
}
.width('100%')
.justifyContent(FlexAlign.Center)
}
build() {
Navigation(){
List({space:10}){
ListItemGroup(){
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_0'),titleString:'朋友圈'})
}
.backgroundColor(Color.White)
}
ListItemGroup(){
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_1'),titleString:'直播'})
}
.backgroundColor(Color.White)
}
ListItemGroup(){
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_2'),titleString:'扫一扫'})
}
.backgroundColor(Color.White)
}
ListItemGroup(){
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_3'),titleString:'看一看'})
}
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_4'),titleString:'搜一搜'})
}
}
.divider({ strokeWidth: 1, color: 'rgb(247,247,247)', startMargin: 0, endMargin: 0 }) // 每行之间的分界线
.backgroundColor(Color.White)
ListItemGroup(){
ListItem(){
DiscoverRow({imagePath:$r('app.media.discover_5'),titleString:'小程序'})
}
.backgroundColor(Color.White)
}
}
.backgroundColor('rgb(237,237,237)')
}
.title(this.NavigationTitle())
.mode(NavigationMode.Stack)
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)
// .menus(this.NavigationMenus())
.size({ width: '100%', height: '100%' })
.backgroundColor('rgb(237,237,237)')
}
}
@Component
export struct DiscoverRow {
imagePath:Resource
titleString:string
build(){
Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center}){
Flex({direction:FlexDirection.Row,alignItems:ItemAlign.Center}){
Image(this.imagePath)
.width(22)
.height(22)
Text(this.titleString)
.fontSize(17)
.margin({left:10})
}
.height(50)
Image($r('app.media.arrow_right'))
.width(20)
.height(20)
}
.height(50)
.padding({left:12,right:12})
}
}
中间部分布局封装
@Component
export struct DiscoverRow {
imagePath:Resource
titleString:string
build(){
Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center}){
Flex({direction:FlexDirection.Row,alignItems:ItemAlign.Center}){
Image(this.imagePath)
.width(22)
.height(22)
Text(this.titleString)
.fontSize(17)
.margin({left:10})
}
.height(50)
Image($r('app.media.arrow_right'))
.width(20)
.height(20)
}
.height(50)
.padding({left:12,right:12})
}
}
最后总结:
鸿蒙的 ark ui 非常类似flutter 这种声明式ui 所有的布局都是代码编写的和传统的布局和逻辑分开有些区别 刚开始上手的时候可能不适应,慢慢就习惯了 还有一点 代码嵌套 这个其实可以把每个组件抽离出来就可以解决鸿蒙的ark ui 网络部分也是类似前端的http 请求 解析json和前端也比较像 也有面向对象编程的思想, 对前端和移动端同学来说也比较好理解和学习。 今天的文章就讲到这里有兴趣的 可以自行下载代码学习。 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 如果了解更多鸿蒙开发的知识 可以关注坚果派公众号 。 谢谢