本示例主要包含:“登录”、“首页”、“我的”三个页面。
完整示例
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
return this.swiperImages
}
getGridData(type: number): Array
switch (type) {
case 0:
return this.firstGridData
break
case 1:
return this.secondGridData
break
default:
return this.firstGridData
break
}
}
getSettingListData(): Array
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等容器组件的使用。
推荐文章
发表评论