HarmonyOS | 项目开发练习 「钢笔单词」 #4 单词学习页 | 涉及弹性布局(FLEX)、用户首选项、关系型数据库
项目结构:
单词学习页:
通过在书架里面点击词书,即可路由到这个页面,主要进行单词选择、单词背诵、拼写以及单词遮盖。可以点击左侧的滑动列表中的单词以切换单词,也可以点击蓝色的按钮进行逐词切换。点击“眼睛”可以显示或隐藏单词,点击“折叠箭头”可以将左侧的滑动列表折叠(真的是折叠)。点击拼写按钮会弹出拼写输入框,同时其它卡片会被折叠(真的是折叠)。
LearningIndex.ets 构成单词学习页面,是的,这个文件非常简短,因为我把代码都写进这里面的那个唯一的模块里了(哭)。
``
import LearningCard from '../view/Learning_page/LearningCard'
@Entry
@Component
struct LearningIndex {
build() {
Flex(){
LearningCard({sum: 40})
}
}
}
LearningCard.ets 单词学习页面几乎在这一个文件里面完成,写的也比较粗糙,而且我没有实现数据库,所以用数组进行替代
``
import router from '@ohos.router'
import Word from '../../model/Word'
import WordModel from '../../viewModel/WordModel'
import SpellingDialog from './SpellingDialog'
@Component
export default struct LearningHeader {
private sum: number // 需要背诵的单词数量
@State index: number = 1 // 当前背诵的单词的序号
words: Word[] = [
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。'),
new Word('occupy', '英/ˈɒkjupaɪ/ \n美/ˈɑːkjupaɪ/', 'v.使用(房屋、建筑),居住;占据(空间,时间);使忙于(做某事);使一直在想;(军事)占领,占据;(尤指为表示抗议而)非法强占(建筑物);任职,位居', '', 'How much memory does the program occupy? 这个程序占用多少内存?'),
new Word('process', '英/ˈprəʊses/ \n美/ˈprɑːses/', 'n.步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。')
]
@State wordDisplay: string = '******'
@State soundmarkDisplay: string = this.words[0].soundmark
@State paraphraseDisplay: string = this.words[0].paraphrase
@State sentenceDisplay: string = this.words[0].sentence
@State leftCardWidth: string = '30%'
@State rightCardWidth: string = '65%'
@State isVisible: boolean = false // 单词可见性
@State eyesIcon: Resource = $r('app.media.cleyes')
controller: CustomDialogController = new CustomDialogController({
builder: SpellingDialog({answer: this.words[this.index - 1].word, cancel: () => this.cancel()})
})
cancel() {
this.leftCardWidth = '30%'
this.rightCardWidth = '65%'
}
handleAddWord(word: string, soundmark: string, paraphrase: string, audioLink: string, sentence: string) {
WordModel.addWord(word, soundmark, paraphrase, audioLink, sentence)
.then(id => {
console.log('新增单词')
this.words.push(new Word(word, soundmark, paraphrase, audioLink, sentence))
})
.catch(e => console.error('新增单词失败-' + word))
}
// aboutToAppear(){
// WordModel.addWord('123', '英/4/ \n美/5/', 'n.步骤,6;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。')
// this.handleAddWord('123', '英/4/ \n美/5/', 'n.步骤,6;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起adj.(印刷)三原色的,三色版的; 经过特殊加工的;照相板的v.(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发', '', 'The process was needlessly slow. 进程过于缓慢了。')
// WordModel.getWordList()
// .then(list => {
// })
// }
build() {
Flex({direction: FlexDirection.Column}){
Flex({alignItems: ItemAlign.Center}){
Image($r('app.media.leacancle')).fillColor('#ff262626').width(50)
.onClick(() => {
router.back()
})
Text(this.index.toString()).fontSize(40)
}
Divider()
Flex({alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly}){
Flex({direction: FlexDirection.Column}){
Text('生词本').fontSize(25)
Flex({direction: FlexDirection.Column}){
List({space: 10}){
ForEach(
this.words,
(word, idx) => {
ListItem() {
Flex({direction: FlexDirection.Column}) {
Divider().color(Color.White)
Text((idx + 1) + ' ' + word.word + ' \n' + word.soundmark).fontSize(30).fontColor(Color.White)
}.padding({left: 20})
.onClick(() => {
if(this.isVisible) this.wordDisplay = this.words[idx].word
else this.wordDisplay = '******'
this.soundmarkDisplay = this.words[idx].soundmark
this.paraphraseDisplay = this.words[idx].paraphrase
this.sentenceDisplay = this.words[idx].sentence
this.index = idx + 1
})
}
})
}.borderRadius(10).layoutWeight(1)
}.backgroundColor('#ff313a4a').width('100%').borderRadius(10)
}.width(this.leftCardWidth).height('90%').borderRadius(10)
.shadow({radius: 5, color: $r('app.color.light_primary_color')})
Flex({direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceAround}){
Text('单词卡片').fontSize(25)
Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Center}){
Flex({alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly}){
Text(this.wordDisplay).fontSize(80).fontColor(Color.White).margin({top: 10}).fontWeight(FontWeight.Bold)
Text(this.soundmarkDisplay).fontSize(25).fontColor(Color.White).margin({top: 10})
}
List(){
ListItem(){
Text(this.paraphraseDisplay).fontSize(30).fontColor(Color.White).margin({top: 10})
}
}.height('30%').backgroundColor('#ff353535').margin({top: 20, bottom: 20}).padding(10)
Text(this.sentenceDisplay).fontSize(30).fontColor(Color.White).margin({top: 10})
.backgroundColor('#ff353535').padding(10)
Flex({justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center}){
Button('上一个')
.onClick(() => {
if(this.index <= 1) return;
let idx = this.index - 2
this.index = this.index - 1
if(this.isVisible) this.wordDisplay = this.words[idx].word
else this.wordDisplay = '******'
this.soundmarkDisplay = this.words[idx].soundmark
this.paraphraseDisplay = this.words[idx].paraphrase
this.sentenceDisplay = this.words[idx].sentence
})
Button('下一个')
.onClick(() => {
if(this.index >= this.words.length) return;
let idx = this.index
this.index = this.index + 1
if(this.isVisible) this.wordDisplay = this.words[idx].word
else this.wordDisplay = '******'
this.soundmarkDisplay = this.words[idx].soundmark
this.paraphraseDisplay = this.words[idx].paraphrase
this.sentenceDisplay = this.words[idx].sentence
})
Image(this.eyesIcon).fillColor('#ffe3e3e3').width(40)
.onClick(() => {
this.isVisible = !this.isVisible
if(this.isVisible) this.wordDisplay = this.words[this.index - 1].word
else this.wordDisplay = '******'
this.soundmarkDisplay = this.words[this.index - 1].soundmark
this.paraphraseDisplay = this.words[this.index - 1].paraphrase
this.sentenceDisplay = this.words[this.index - 1].sentence
if(this.isVisible) this.eyesIcon = $r('app.media.eyes')
else this.eyesIcon = $r('app.media.cleyes')
})
Image($r('app.media.spell')).fillColor('#ffe3e3e3').width(40)
.onClick(() => {
this.leftCardWidth = '10'
this.rightCardWidth = '10'
this.controller.open()
})
Image($r('app.media.zhedie')).fillColor('#ffe3e3e3').width(40)
.onClick(() => {
if(this.leftCardWidth === '30%'){
this.leftCardWidth = '10'
this.rightCardWidth = '85%'
}
else {
this.leftCardWidth = '30%'
this.rightCardWidth = '65%'
}
})
}.height('20%')
}.backgroundColor('#ff414141').width('100%').borderRadius(10)
}.width(this.rightCardWidth).height('90%').borderRadius(10)
.shadow({radius: 5, color: $r('app.color.light_primary_color')})
}.height('100%')
}
}
}
SpellingDialog.ets 拼写弹窗,这是一个自定义弹窗
``
import CommonConstants from '../../common/constants/CommonConstants'
@CustomDialog
export default struct UserPrivacyDialog {
controller: CustomDialogController
cancel: () => void
private answer: string // 正确答案
@State userInput: string = '请输入单词' // 用户输入
confirm() {
if (this.answer === this.userInput)
this.userInput = '正确'
else this.userInput = '错误'
}
build() {
Column({space: CommonConstants.SPACE_10}){
Text('拼写').fontSize(20).fontWeight(CommonConstants.FONT_WEIGHT_700) // 隐私弹窗标题
Divider().color($r('app.color.primary_color'))
TextInput()
.onChange((val) => {
this.userInput = val
})
Text(this.userInput).fontColor('#ff858585')
// Text(this.answer).fontColor('#ff858585')
Button('检验拼写').backgroundColor($r('app.color.primary_color'))
.width('50%').onClick(() =>{
this.confirm()
}) // 确定按钮
Button('关闭窗口').backgroundColor($r('app.color.lightest_primary_color'))
.fontColor($r('app.color.light_gray')).width('50%').onClick(() => {
this.controller.close()
this.cancel()
}) // 返回按钮
}.width('100%').padding(10).border({width: 1, color: $r('app.color.primary_color'), radius: 10})
}
}