项目源码已发布到GitCode平台, 方便开发者进行下载和使用。
https://gitcode.com/qq_33681891/NovelReader
在移动应用开发中,底部菜单栏是用户交互的重要组成部分,尤其对于阅读类应用来说,一个功能完善、交互友好的底部菜单栏能够极大提升用户体验。本教程将详细讲解如何在HarmonyOS应用中实现一个功能丰富的阅读应用底部菜单栏,包括翻页方式选择、界面设置等功能。
本教程涉及以下HarmonyOS开发技术点:
我们的底部菜单栏主要包含以下几个部分:
首先,我们需要定义组件的基本结构和状态变量:
@Component
export struct BottomView {
// @Link装饰器:父子双向同步。@Link装饰的变量与其父组件中的数据源共享相同的值。
@Link filledName: string; // 当前选中的底部图标名称
@Link buttonClickedName: string; // 点击按钮的名称,用来判断是否已被点击
@Link isVisible: boolean; // 用来判断翻页方式视图是否显示
@Link isCommentVisible: boolean; // 用来判断评论视图是否显示
@Link isMenuViewVisible: boolean; // 用来判断上下菜单视图是否显示
@State buttonNameList: Array<string> = [STRINGCONFIGURATION.LEFTRIGHTFLIPPAGENAME,
STRINGCONFIGURATION.UPDOWNFLIPPAGENAME,
STRINGCONFIGURATION.COVERFLIPPAGENAME]
@Link bgColor: string; // 背景颜色
@Link textSize: number; // 文本大小
@State isTextReader: boolean = false; // 文本朗读状态
@State isTextSize: boolean = false; // 字体大小状态
@State isbgColor: boolean = false; // 背景颜色状态
@Link isbgImage: boolean; // 背景图片状态
@Link currentPageNum: number; // 当前页码
// 播放文章列表
@Prop readInfoList: TextReader.ReadInfo[] = [];
@Prop selectedReadInfo: TextReader.ReadInfo = this.readInfoList[0];
// 用于显示当前页的按钮状态
@State isInit: boolean = false; // 初始化状态
}
这里使用了HarmonyOS的几种状态装饰器:
@Link
:实现父子组件间的双向数据同步@State
:管理组件内部状态@Prop
:接收父组件传递的只读数据底部图标栏是用户最常交互的区域,包含了多个功能入口:
Row() {
Image(this.filledName === STRINGCONFIGURATION.PAGEFLIPVIEWLIST ?
$r('app.media.flippage_view_list_filled') : $r('app.media.flippage_view_list'))
.width($r('app.string.pageflip_bottomview_row_image_width'))
.height($r('app.string.pageflip_bottomview_row_image_height'))
.objectFit(ImageFit.Contain)
.padding($r('app.integer.flippage_padding_small'))
.onClick(() => {
this.clickAnimate(STRINGCONFIGURATION.PAGEFLIPVIEWLIST);
})
// 其他图标(自由阅读、发现、亮度、设置)的实现类似
// ...
Image(this.filledName === STRINGCONFIGURATION.PAGEFLIPSETTINGS ?
$r('app.media.flippage_settings_filled') : $r('app.media.flippage_settings'))
.width($r('app.string.pageflip_bottomview_row_image_width'))
.height($r('app.string.pageflip_bottomview_row_image_height'))
.objectFit(ImageFit.Contain)
.padding($r('app.integer.flippage_padding_small'))
.onClick(() => {
animateTo({
duration: CONFIGURATION.PAGEFLIPTOASTDURATION,
curve: Curve.Linear,
}, () => {
if (this.filledName === STRINGCONFIGURATION.PAGEFLIPSETTINGS) {
this.filledName = '';
this.isVisible = false;
this.isCommentVisible = true;
} else {
this.filledName = STRINGCONFIGURATION.PAGEFLIPSETTINGS;
this.isVisible = true;
this.isCommentVisible = false;
}
});
})
.id('setting')
}
这里的关键点:
翻页方式选择面板允许用户选择不同的翻页模式:
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.buttonNameList, (item: string) => {
Button(item, { type: ButtonType.Capsule })
.backgroundColor($r('app.color.pageflip_button_backgroundcolor'))
.fontColor(this.buttonClickedName === item ?
$r('app.color.pageflip_button_click_fontcolor') :
$r('app.color.pageflip_button_fontcolor'))
.margin({ left: $r('app.integer.flippage_margin_small'),
right: $r('app.integer.flippage_margin_small') })
.borderWidth(CONFIGURATION.PAGEFLIPBORDERWIDTH)
.onClick(() => {
if (this.buttonClickedName !== item) {
this.buttonClickedName = item;
this.isMenuViewVisible = false;
this.filledName = '';
this.isVisible = false;
}
})
}, (item: string) => item)
}
.margin({ top: $r('app.integer.flippage_margin_small'),
bottom: $r('app.integer.flippage_margin_small') })
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
这里的关键点:
ForEach
循环渲染按钮列表visibility
属性控制面板的显示和隐藏阅读设置面板提供了文本朗读、字体大小、背景颜色等功能:
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
Button($r('app.string.pageflip_button_text_reader'), { type: ButtonType.Capsule })
.backgroundColor($r('app.color.pageflip_button_backgroundcolor'))
.fontColor(this.isTextReader ?
$r('app.color.pageflip_button_click_fontcolor') :
$r('app.color.pageflip_button_fontcolor'))
.margin({ left: $r('app.integer.flippage_margin_small'),
right: $r('app.integer.flippage_margin_small') })
.borderWidth(CONFIGURATION.PAGEFLIPBORDERWIDTH)
.onClick( () => {
this.setEventListener();
if(!this.isTextReader) {
// 朗读控件起播,拉起播放器面板并开始播放
TextReader.showPanel();
TextReader.start(this.readInfoList, this.selectedReadInfo?.id).then(() => {
logger.info('TextReader succeeded in starting');
}).catch((e: BusinessError) => {
logger.error(`TextReader failed to start. Code: ${e.code}, message: ${e.message}`);
})
}
else {
// 朗读控件停止朗读,执行播放面板的关闭
TextReader.stop().then(() => {
logger.info(`item TextReader succeeded in stopping.`);
}).catch((e: BusinessError) => {
logger.error(`TextReader failed to stop. Code: ${e.code}, message: ${e.message}`);
})
TextReader.hidePanel();
}
this.isTextReader = !this.isTextReader;
emitter.emit({ eventId: 0, priority: 0 }, {
data: {
isTextReader: this.isTextReader
}
})
this.isMenuViewVisible = false;
this.filledName = '';
this.isVisible = false;
})
.id('textReading')
// 字体大小和背景颜色按钮实现类似
// ...
}
.margin({ top: $r('app.integer.flippage_margin_small'),
bottom: $r('app.integer.flippage_margin_small') })
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
这里的关键点:
emitter
发送事件通知其他组件状态变化为了增强用户体验,我们为菜单切换添加了动画效果:
clickAnimate(name: string) {
animateTo({
duration: CONFIGURATION.PAGEFLIPTOASTDURATION,
curve: Curve.Linear,
}, () => {
if (this.filledName === name) {
this.filledName = '';
this.isCommentVisible = true;
} else {
promptAction.showToast({
message: $r('app.string.pageflip_default_toast'),
duration: CONFIGURATION.PAGEFLIPTOASTDURATION
});
this.filledName = name;
this.isVisible = false;
this.isCommentVisible = false;
}
})
}
这里的关键点:
animateTo
实现平滑的状态过渡promptAction.showToast
提供用户反馈文本朗读是阅读应用的重要功能,我们使用HarmonyOS的TextReader
组件实现:
async init() {
// 设置朗读参数
const readerParam: TextReader.ReaderParam = {
isVoiceBrandVisible: true,
businessBrandInfo: {
panelName: STRINGCONFIGURATION.XIAOYIREADING
}
}
try{
// 初始化朗读控件
await TextReader.init(getContext(this), readerParam);
this.isInit = true;
} catch (err) {
logger.error(`TextReader failed to init. Code: ${err.code}, message: ${err.message}`);
}
}
setEventListener(){
TextReader.on('stop', () => {
this.isTextReader = false;
});
TextReader.on('stateChange', (state: TextReader.ReadState) => {
// 当前正在播放的文章播放完成
if(state.state === CONFIGURATION.COMPLETED) {
// 当朗读的为最后一页
if(Number(state.id) === this.readInfoList.length) {
this.currentPageNum = Number(state.id);
} else {
this.currentPageNum = Number(state.id) + 1;
}
}
});
TextReader.on('eventPanel', (pe: TextReader.PanelEvent) => {
// 点击上一条按钮
if(pe.click === 'BPC_03') {
this.currentPageNum = Number(pe.id);
// 点击下一条按钮
} else if(pe.click === 'BPC_04') {
this.currentPageNum = Number(pe.id);
}
});
}
@Link
、@State
和@Prop
装饰器管理组件状态visibility
属性控制UI元素的显示和隐藏animateTo
实现平滑的状态过渡$r
引用应用资源,便于国际化和主题切换通过本教程,我们学习了如何在HarmonyOS应用中实现一个功能完善的阅读应用底部菜单栏。这个菜单栏不仅提供了基本的导航功能,还集成了翻页方式选择、文本朗读、字体大小调整、背景颜色设置等高级功能,大大提升了用户的阅读体验。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有