本示例主要包含:“登录”、“首页”、“我的”三个页面。
完整示例
harmonyos-ui: harmonyos一些ui示例 - gitee.com
软件要求
- deveco studio版本:deveco studio 3.1 release。
- harmonyos sdk版本:api version 9。
代码结构
- ├──entry/src/main/ets // 代码区
- │ ├──common
- │ │ └──constants
- │ │ └──commonconstants.ets // 公共常量类
- │ ├──entryability
- │ │ └──entryability.ts // 程序入口类
- │ ├──pages
- │ │ ├──login.ets // 登录界面
- │ │ └──mainpage.ets // 主界面
- │ ├──view
- │ │ ├──home.ets // 首页
- │ │ └──mime.ets // 我的
- │ └──viewmodel
- │ ├──itemdata.ets // 列表数据实体类
- │ └──mainviewmodel.ets // 主界面视图model
- └──entry/src/main/resources // 应用资源目录
实现“登录”页面
页面使用column容器组件布局,由image、text、textinput、button、loadingprogress等基础组件构成。
代码整体结构
@entry
@component
struct loginpage {
...
build() {
column() {
image($r('app.media.logo'))
...
text($r('app.string.login_page'))
...
text($r('app.string.login_more'))
...
textinput({ placeholder: $r('app.string.account') })
...
textinput({ placeholder: $r('app.string.password') })
...
button($r('app.string.login'), { type: buttontype.capsule })
...
if (this.isshowprogress) {
loadingprogress()
...
}
...
}
...
}
}
获取用户输入
当用户登录前,需要获取用户输入的帐号和密码才能执行登录逻辑。给textinput设置onchange事件,在onchange事件里面实时获取用户输入的文本信息。
控制loadingprogress显示和隐藏
给登录按钮绑定onclick事件,调用login方法模拟登录。定义变量isshowprogress结合条件渲染if用来控制loadingprogress的显示和隐藏。当用户点击按钮时设置isshowprogress为true,即显示loadingprogress;使用定时器settimeout设置isshowprogress 2秒后为false,即隐藏loadingprogress,然后执行跳转到首页的逻辑。
实现页面跳转
页面间的跳转可以使用router模块相关api来实现,使用前需要先导入该模块,然后使用router.replaceurl()方法实现页面跳转。
login(): void {
...
this.isshowprogress = true;
if (this.timeoutid === -1) {
this.timeoutid = settimeout(() => {
this.isshowprogress = false;
this.timeoutid = -1;
router.replaceurl({ url: 'pages/mainpage' });
}, commonconstants.login_delay_time);
}
}
login完整代码
import prompt from '@system.prompt';
import router from '@ohos.router';
import commonconstants from '../common/constants/commonconstants'
@entry
@component
struct login {
@state account: string = '';
@state password: string = '';
@state isshowprogress: boolean = false
private timeoutid: number = -1
@builder
imagebutton(src: resource) {
button({ type: buttontype.circle, stateeffect: true }) {
image(src)
}
.height($r('app.float.other_login_image_size'))
.width($r('app.float.other_login_image_size'))
.backgroundcolor($r('app.color.background'))
.margin({bottom:$r('app.float.other_login_margin_bottom')})
}
login(): void {
if (this.account === '' || this.password === '') {
prompt.showtoast({
message: commonconstants.input_not_empty
})
} else {
this.isshowprogress = true
if (this.timeoutid === -1) {
this.timeoutid = settimeout(() => {
this.isshowprogress = false
this.timeoutid = -1
router.replaceurl({ url: 'pages/mainpage' })
}, commonconstants.login_delay_time)
}
}
}
build() {
column() {
image($r('app.media.logo'))
.width($r('app.float.logo_image_size'))
.height($r('app.float.logo_image_size'))
.margin({ top: $r('app.float.logo_margin_top'), bottom: $r('app.float.logo_margin_bottom') })
text($r('app.string.login_module_desc_no'))
.fontsize($r('app.float.page_title_text_size'))
.fontweight(fontweight.medium)
.fontcolor($r('app.color.login_text_color'))
text($r('app.string.login_module_desc_de'))
.fontsize($r('app.float.normal_text_size'))
.fontweight(fontweight.medium)
.fontcolor($r('app.color.login_more_text_color'))
.margin({ bottom: $r('app.float.login_more_margin_bottom'), top: $r('app.float.login_more_margin_top') })
textinput({ placeholder: $r('app.string.account') })
.maxlength(commonconstants.input_account_length)
.inputstyle()
.onchange((value: string) => {
this.account = value
})
line().linestyle()
textinput({ placeholder: $r('app.string.password') })
.maxlength(commonconstants.input_password_length)
.inputstyle()
.type(inputtype.password)
.onchange((value: string) => {
this.password = value
})
line().linestyle()
row({ space: commonconstants.common_space_20 }) {
text($r('app.string.message_login')).bluetextstyle()
text($r('app.string.forgot_password')).bluetextstyle()
}
.justifycontent(flexalign.spacebetween)
.width(commonconstants.page_width)
button($r('app.string.login'), { type: buttontype.capsule })
.width(commonconstants.button_width)
.height($r('app.float.login_button_height'))
.fontsize($r('app.float.normal_text_size'))
.fontweight(fontweight.medium)
.backgroundcolor($r('app.color.login_button_color'))
.margin({ top: $r('app.float.login_button_margin_top'), bottom: $r('app.float.login_button_margin_bottom') })
.onclick(() => {
this.login()
})
text($r('app.string.register_account'))
.fontcolor($r('app.color.login_blue_text_color'))
.fontsize($r('app.float.normal_text_size'))
.fontweight(fontweight.medium)
if (this.isshowprogress) {
loadingprogress()
.color($r('app.color.loading_color'))
.width($r('app.float.login_progress_size'))
.height($r('app.float.login_progress_size'))
.margin({ top: $r('app.float.login_progress_margin_top') })
}
blank()
text($r('app.string.other_login_method'))
.fontcolor($r('app.color.other_login_text_color'))
.fontsize($r('app.float.little_text_size'))
.fontweight(fontweight.medium)
.margin({ top: $r('app.float.other_login_margin_top'), bottom: $r('app.float.other_login_margin_bottom') })
row({ space: commonconstants.login_methods_space }) {
this.imagebutton($r('app.media.login_method1'))
this.imagebutton($r('app.media.login_method2'))
this.imagebutton($r('app.media.login_method3'))
}
}
.backgroundcolor($r('app.color.background'))
.height(commonconstants.full_parent)
.width(commonconstants.full_parent)
.padding({
left: $r('app.float.page_padding_hor'),
right: $r('app.float.page_padding_hor'),
bottom: $r('app.float.login_page_padding_bottom')
})
}
}
@extend(textinput) function inputstyle() {
.placeholdercolor($r('app.color.placeholder_color'))
.height($r('app.float.login_input_height'))
.fontsize($r('app.float.big_text_size'))
.backgroundcolor($r('app.color.background'))
.width(commonconstants.full_parent)
.margin({ top: $r('app.float.input_margin_top') })
}
@extend(text) function bluetextstyle() {
.fontcolor($r('app.color.login_blue_text_color'))
.fontsize($r('app.float.small_text_size'))
.fontweight(fontweight.medium)
.margin({ top: $r('app.float.forgot_margin_top') })
}
@extend(line) function linestyle() {
.width(commonconstants.full_parent)
.height($r('app.float.line_height'))
.backgroundcolor($r('app.color.line_color'))
}
实现“首页”和“我的”页面
本示例由两个tab页组成,使用tabs组件来实现,提取tabbar的公共样式,同时设置tabcontent和tabs的backgroundcolor来实现底部tabbar栏背景色突出的效果。
定义资源数据
由于“首页”和“我的”页面中有多处图片和文字的组合,因此提取出itemdata类。在mainviewmodel.ets文件中对页面使用的资源进行定义。
mainviewmodel代码
import { itemdata } from './itemdata';
export class mainviewmodel {
private swiperimages: resource[] = [
$r('app.media.fig1'),
$r('app.media.fig2'),
$r('app.media.fig3'),
$r('app.media.fig4')
];
private firstgriddata: itemdata[] = [
new itemdata($r('app.string.my_love'), $r('app.media.love')),
new itemdata($r('app.string.history_record'), $r('app.media.record')),
new itemdata($r('app.string.message'), $r('app.media.message')),
new itemdata($r('app.string.shopping_cart'), $r('app.media.shopping')),
new itemdata($r('app.string.my_goal'), $r('app.media.target')),
new itemdata($r('app.string.group'), $r('app.media.circle')),
new itemdata($r('app.string.favorites'), $r('app.media.favorite')),
new itemdata($r('app.string.recycle_bin'), $r('app.media.recycle'))
];
private secondgriddata: itemdata[] = [
new itemdata($r('app.string.mainpage_top'), $r('app.media.top'), $r('app.string.mainpage_text_top')),
new itemdata($r('app.string.mainpage_new'), $r('app.media.new'), $r('app.string.mainpage_text_new')),
new itemdata($r('app.string.mainpage_brand'), $r('app.media.brand'), $r('app.string.mainpage_text_brand')),
new itemdata($r('app.string.mainpage_found'), $r('app.media.found'), $r('app.string.mainpage_text_found')),
];
private settinglistdata: itemdata[] = [
new itemdata($r('app.string.setting_list_news'), $r('app.media.news'), $r("app.string.setting_toggle")),
new itemdata($r('app.string.setting_list_data'), $r('app.media.data')),
new itemdata($r('app.string.setting_list_menu'), $r('app.media.menu')),
new itemdata($r('app.string.setting_list_about'), $r('app.media.about')),
new itemdata($r('app.string.setting_list_storage'), $r('app.media.storage')),
new itemdata($r('app.string.setting_list_privacy'), $r('app.media.privacy'))
];
getswiperimags(): array<resource> {
return this.swiperimages
}
getgriddata(type: number): array<itemdata> {
switch (type) {
case 0:
return this.firstgriddata
break
case 1:
return this.secondgriddata
break
default:
return this.firstgriddata
break
}
}
getsettinglistdata(): array<itemdata>{
return this.settinglistdata
}
}
export default new mainviewmodel()
itemdata代码
export class itemdata{
/**
* text of list item.
*/
title: resource;
/**
* image of list item.
*/
img: resource;
/**
* other resource of list item.
*/
others?: resource;
constructor(title: resource, img: resource, others?: resource) {
this.title = title;
this.img = img;
this.others = others;
}
}
实现“首页”内容
从效果图可以看出“首页”由三部分内容组成分别是轮播图、2*4栅格图、2*2栅格图。首先使用swiper组件实现轮播图,无需设置图片大小。
然后使用grid组件实现2*4栅格图。
使用grid组件实现2*2栅格列表栏,其中单个栅格中有一张背景图片和两行字体不同的文本,因此在column组件中放置两个text组件,并设置背景图,注意grid组件必须设置高度,否则可能出现页面空白。
mainpage代码
import router from '@ohos.router'
import commonconstants from '../common/constants/commonconstants'
import home from '../view/home'
import mime from '../view/mime'
@entry
@component
struct mainpage {
@state fontcolor: string = '#182431'
@state selectedfontcolor: string = '#007dff'
@state currentindex: number = 0
private controller: tabscontroller = new tabscontroller()
@builder tabbuilder(title: string, index: number, selectedimg: resource, normalimg: resource) {
column() {
image(this.currentindex === index ? selectedimg : normalimg)
.width($r('app.float.mainpage_basetab_size'))
.height($r('app.float.mainpage_basetab_size'))
.objectfit(imagefit.contain)
text(title)
.fontcolor(this.currentindex === index ? $r('app.color.mainpage_selected') : $r('app.color.mainpage_normal'))
.fontsize($r('app.float.main_tab_fontsize'))
}
.width('100%')
}
build() {
column() {
tabs({ barposition: barposition.end, controller: this.controller }) {
tabcontent() {
home()
}.tabbar(this.tabbuilder(
commonconstants.home_title,
commonconstants.home_tab_index,
$r('app.media.home_selected'),
$r('app.media.home_normal')))
tabcontent() {
mime()
}.tabbar(this.tabbuilder(
commonconstants.mine_title,
commonconstants.mine_tab_index,
$r('app.media.mine_selected'),
$r('app.media.mine_normal')))
}
.vertical(false)
.width(commonconstants.full_parent)
.backgroundcolor(color.white)
.barheight($r('app.float.mainpage_barheight'))
.barmode(barmode.fixed)
.onchange((index: number) => {
this.currentindex = index
/* if(this.currentindex == 1){
router.pushurl({
url:'./pages/mime'
})
}*/
})
}
.width(commonconstants.full_parent)
.height(commonconstants.full_parent)
.justifycontent(flexalign.end)
}
}
home代码
import commonconstants from '../common/constants/commonconstants'
import { itemdata } from '../viewmodel/itemdata'
import mainviewmodel from '../viewmodel/mainviewmodel'
@component
export default struct home {
private swipercontroller: swipercontroller = new swipercontroller()
private gridscroller: scroller = new scroller()
build() {
column({ space: commonconstants.common_space }) {
column() {
text($r('app.string.mainpage_tabtitles_home'))
.fontweight(fontweight.medium)
.fontsize($r('app.float.page_title_text_size'))
.margin({ top: $r('app.float.mainpage_tabtitles_margin') })
.padding({ left: $r('app.float.mainpage_tabtitles_padding') })
}
.width(commonconstants.full_parent)
.alignitems(horizontalalign.start)
swiper(this.swipercontroller) {
foreach(mainviewmodel.getswiperimags(), (img: resource) => {
image(img).borderradius($r('app.float.home_swiper_borderradius'))
}, (img: resource) => json.stringify(img.id))
}
.width(commonconstants.full_parent)
.height($r('app.float.home_swiper_height'))
.margin({ top: $r('app.float.home_swiper_margin') })
.autoplay(true)
grid() {
foreach(mainviewmodel.getgriddata(0), (item: itemdata) => {
griditem() {
column() {
image(item.img)
.width($r('app.float.home_homecell_size'))
.height($r('app.float.home_homecell_size'))
text(item.title)
.fontsize($r('app.float.little_text_size'))
.margin({ top: $r('app.float.home_homecell_margin') })
}
}
}, (item: itemdata) => json.stringify(item))
}
.columnstemplate('1fr 1fr 1fr 1fr')
.rowstemplate('1fr 1fr')
.columnsgap($r('app.float.home_grid_columnsgap'))
.rowsgap($r('app.float.home_grid_rowgap'))
.padding({ top: $r('app.float.home_grid_padding'), bottom: $r('app.float.home_grid_padding') })
.height($r('app.float.home_grid_height'))
.backgroundcolor(color.white)
.borderradius($r('app.float.home_grid_borderradius'))
text($r('app.string.home_list'))
.fontsize($r('app.float.normal_text_size'))
.fontweight(fontweight.medium)
.width(commonconstants.common_width_90)
.margin({ top: $r('app.float.home_text_margin') })
grid(this.gridscroller) {
foreach(mainviewmodel.getgriddata(1), (seconditem: itemdata) => {
griditem() {
column() {
text(seconditem.title)
.fontsize($r('app.float.normal_text_size'))
.fontweight(fontweight.medium)
text(seconditem.others)
.margin({ top: $r('app.float.home_list_margin') })
.fontsize($r('app.float.little_text_size'))
.fontcolor($r('app.color.home_grid_fontcolor'))
}
.alignitems(horizontalalign.start)
}
.padding({ top: $r('app.float.home_list_padding'), left: $r('app.float.home_list_padding') })
.borderradius($r('app.float.home_backgroundimage_borderradius'))
.align(alignment.topstart)
.backgroundimage(seconditem.img)
.backgroundimagesize(imagesize.cover)
.width(commonconstants.full_parent)
.height(commonconstants.full_parent)
}, (seconditem: itemdata) => json.stringify(seconditem))
}
.width(commonconstants.common_width_90)
.height($r('app.float.home_secondgrid_height'))
.columnstemplate('1fr 1fr')
// .rowstemplate('1fr 1fr')
.columnsgap($r('app.float.home_grid_columnsgap'))
.rowsgap($r('app.float.home_grid_rowgap'))
.layoutweight(1)
.onscrollindex((first: number) => {
console.info('111111111',first.tostring())
})
}
.height(commonconstants.full_parent)
}
}
实现“我的”页面内容
使用list组件结合foreach语句来实现页面列表内容。
mime代码
import commonconstants from '../common/constants/commonconstants'
import { itemdata } from '../viewmodel/itemdata'
import mainviewmodel from '../viewmodel/mainviewmodel'
@component
export default struct mime {
@builder settingcell(item: itemdata) {
column() {
row() {
row({ space: commonconstants.common_space_10 }) {
image(item.img)
.width($r('app.float.setting_size'))
.height($r('app.float.setting_size'))
text(item.title)
.fontsize($r('app.float.normal_text_size'))
}
if (item.others === null || item.others === undefined) {
image($r('app.media.right_grey'))
.width($r('app.float.setting_jump_width'))
.height($r('app.float.setting_jump_height'))
} else {
toggle({ type: toggletype.switch, ison: false })
}
}
.justifycontent(flexalign.spacebetween)
.width(commonconstants.full_parent)
}
}
build() {
column({ space: commonconstants.common_space_10 }) {
text('我的')
.width(commonconstants.common_width_90)
.height(50)
.fontsize(24)
.fontweight(600)
row() {
image($r('app.media.account'))
.width(50)
.height(50)
column() {
text($r('app.string.setting_account_name'))
.fontsize($r('app.float.setting_account_fontsize'))
.fontweight(400)
text($r('app.string.setting_account_email'))
.fontsize($r('app.float.little_text_size'))
.margin({ top: $r('app.float.setting_name_margin') })
}
.alignitems(horizontalalign.start)
.margin({ left: $r('app.float.setting_padding') })
}
.margin({ top: $r('app.float.login_progress_margin_top') })
.width(commonconstants.common_width_80)
.justifycontent(flexalign.start)
list() {
foreach(mainviewmodel.getsettinglistdata(), (item: itemdata) => {
listitem() {
this.settingcell(item)
}.height($r('app.float.setting_list_height'))
}, (item: itemdata) => json.stringify(item))
}
.backgroundcolor(color.white)
.width(commonconstants.common_width_90)
.height(commonconstants.common_width_50)
.margin({ top: $r('app.float.other_login_margin_top') })
.borderradius($r('app.float.setting_list_borderradius'))
.padding({ top: $r('app.float.setting_list_padding'), bottom: $r('app.float.setting_list_padding') })
blank()
button($r('app.string.setting_button'), { type: buttontype.capsule })
.width(commonconstants.button_width)
.height($r('app.float.login_button_height'))
.fontsize($r('app.float.normal_text_size'))
.fontcolor($r('app.color.setting_button_fontcolor'))
.fontweight(fontweight.medium)
.backgroundcolor($r('app.color.setting_button_backgroundcolor'))
.margin({ bottom: $r('app.float.setting_button_bottom') })
}
.width(commonconstants.full_parent)
.height(commonconstants.full_parent)
}
}
总结
知识点:
- button、image、textinput、text等基础组件的使用。
- column、row、grid、list、tabs等容器组件的使用。
发表评论