Harmonyos应用实例94:分数加减法训练器
·
应用实例四:分数加减法训练器
知识点:掌握同分母分数加减法,理解异分母分数加减法的算理(通分)。
功能:系统随机生成分数加减法题目。对于同分母题目,直接输入结果;对于异分母题目,应用会分步引导:先提示寻找公分母,再展示通分过程,最后计算结果。设有计时和评分功能,适合课堂练习。
// FractionAddSubPractice.ets
interface Fraction {
numerator: number
denominator: number
}
interface Question {
num1: Fraction
num2: Fraction
operator: string
isSameDenominator: boolean
answer: Fraction
steps: StepInfo[]
}
interface StepInfo {
title: string
content: string
type: string
}
@Entry
@Component
struct FractionAddSubPractice {
@State currentQuestion: Question | null = null
@State userAnswer: string = ''
@State currentStep: number = 0
@State showSteps: boolean = false
@State score: number = 0
@State totalQuestions: number = 0
@State correctCount: number = 0
@State timeElapsed: number = 0
@State isTimerRunning: boolean = false
@State showResult: boolean = false
@State isCorrect: boolean = false
@State practiceMode: string = 'practice'
@State difficulty: string = 'mixed'
private timer: number = -1
aboutToAppear(): void {
this.generateNewQuestion()
}
aboutToDisappear(): void {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
build() {
Column({
space: 15
}) {
Row() {
Text('分数加减法练习')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
.layoutWeight(1)
Text(`⏱️ ${this.formatTime(this.timeElapsed)}`)
.fontSize(16)
.fontColor('#E74C3C')
}
.width('95%')
Row({
space: 15
}) {
Column() {
Text('得分')
.fontSize(12)
.fontColor('#7F8C8D')
Text(`${this.score}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#27AE60')
}
.layoutWeight(1)
.padding(10)
.backgroundColor('#E8F5E9')
.borderRadius(8)
Column() {
Text('正确率')
.fontSize(12)
.fontColor('#7F8C8D')
Text(`${this.totalQuestions > 0 ? Math.round(this.correctCount / this.totalQuestions * 100) : 0}%`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#3498DB')
}
.layoutWeight(1)
.padding(10)
.backgroundColor('#E3F2FD')
.borderRadius(8)
Column() {
Text('题数')
.fontSize(12)
.fontColor('#7F8C8D')
Text(`${this.totalQuestions}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#9B59B6')
}
.layoutWeight(1)
.padding(10)
.backgroundColor('#F3E5F5')
.borderRadius(8)
}
.width('95%')
if (this.currentQuestion) {
Column() {
Text('当前题目')
.fontSize(14)
.fontColor('#7F8C8D')
.margin({ bottom: 10 })
Row({
space: 10
}) {
this.FractionDisplay(this.currentQuestion.num1)
Text(this.currentQuestion.operator)
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
this.FractionDisplay(this.currentQuestion.num2)
Text('=')
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
Text('?')
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#E74C3C')
}
.justifyContent(FlexAlign.Center)
if (this.currentQuestion.isSameDenominator) {
Text('💡 同分母分数,直接计算')
.fontSize(14)
.fontColor('#27AE60')
.margin({ top: 10 })
} else {
Text('💡 异分母分数,需要通分')
.fontSize(14)
.fontColor('#E74C3C')
.margin({ top: 10 })
}
}
.width('95%')
.padding(20)
.backgroundColor('#FFF9C4')
.borderRadius(12)
if (!this.showSteps && !this.showResult) {
Column() {
Text('输入答案(格式:分子/分母)')
.fontSize(14)
.fontColor('#7F8C8D')
.margin({ bottom: 10 })
TextInput({ text: this.userAnswer, placeholder: '例如:3/4' })
.width('80%')
.height(50)
.fontSize(20)
.textAlign(TextAlign.Center)
.backgroundColor('#FFFFFF')
.onChange((value: string) => {
this.userAnswer = value
})
Row({
space: 15
}) {
Button('提交答案')
.fontSize(16)
.height(44)
.backgroundColor('#3498DB')
.onClick(() => this.checkAnswer())
if (!this.currentQuestion.isSameDenominator) {
Button('查看步骤')
.fontSize(16)
.height(44)
.backgroundColor('#9B59B6')
.onClick(() => this.showStepByStep())
}
}
.margin({ top: 15 })
}
.width('95%')
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
if (this.showSteps && this.currentQuestion.steps.length > 0) {
Column() {
Text('📝 解题步骤')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
.margin({ bottom: 15 })
ForEach(this.currentQuestion.steps.slice(0, this.currentStep + 1), (step: StepInfo, index: number) => {
Column() {
Text(`步骤 ${index + 1}: ${step.title}`)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#3498DB')
Text(step.content)
.fontSize(13)
.fontColor('#2C3E50')
.margin({ top: 5 })
}
.width('100%')
.padding(10)
.backgroundColor('#E3F2FD')
.borderRadius(8)
.margin({ bottom: 10 })
.alignItems(HorizontalAlign.Start)
})
if (this.currentStep < this.currentQuestion.steps.length - 1) {
Button('下一步')
.fontSize(14)
.height(40)
.backgroundColor('#27AE60')
.onClick(() => this.nextStep())
} else {
Column() {
Text('现在你知道怎么做了,请输入答案:')
.fontSize(14)
.fontColor('#7F8C8D')
.margin({ bottom: 10 })
TextInput({ text: this.userAnswer, placeholder: '例如:3/4' })
.width('80%')
.height(44)
.fontSize(18)
.textAlign(TextAlign.Center)
.backgroundColor('#FFFFFF')
.onChange((value: string) => {
this.userAnswer = value
})
Button('提交答案')
.fontSize(14)
.height(40)
.backgroundColor('#3498DB')
.margin({ top: 10 })
.onClick(() => this.checkAnswer())
}
.width('100%')
.padding(10)
.backgroundColor('#E8F5E9')
.borderRadius(8)
}
}
.width('95%')
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(12)
.alignItems(HorizontalAlign.Start)
}
if (this.showResult) {
Column() {
if (this.isCorrect) {
Text('✅ 回答正确!')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#27AE60')
} else {
Column() {
Text('❌ 回答错误')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#E74C3C')
Text(`正确答案: ${this.currentQuestion.answer.numerator}/${this.currentQuestion.answer.denominator}`)
.fontSize(16)
.fontColor('#2C3E50')
.margin({ top: 10 })
}
}
Button('下一题')
.fontSize(16)
.height(44)
.backgroundColor('#27AE60')
.margin({ top: 15 })
.onClick(() => this.nextQuestion())
}
.width('95%')
.padding(20)
.backgroundColor(this.isCorrect ? '#E8F5E9' : '#FFEBEE')
.borderRadius(12)
}
}
Column() {
Text('📖 学习提示')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
Text('同分母分数相加减:分母不变,分子相加减')
.fontSize(12)
.fontColor('#7F8C8D')
.margin({ top: 5 })
Text('异分母分数相加减:先通分,再按同分母计算')
.fontSize(12)
.fontColor('#7F8C8D')
.margin({ top: 3 })
}
.width('90%')
.padding(12)
.backgroundColor('#FFF9C4')
.borderRadius(8)
.alignItems(HorizontalAlign.Start)
.margin({ top: 10 })
}
.width('100%')
.height('100%')
.padding(15)
}
@Builder
FractionDisplay(frac: Fraction) {
Column() {
Text(`${frac.numerator}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
Divider()
.width(40)
.color('#2C3E50')
.strokeWidth(2)
Text(`${frac.denominator}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#2C3E50')
}
.width(50)
}
private formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}
private startTimer(): void {
if (this.timer !== -1) {
clearInterval(this.timer)
}
this.isTimerRunning = true
this.timer = setInterval(() => {
this.timeElapsed++
}, 1000)
}
private generateNewQuestion(): void {
const isAddition = Math.random() > 0.5
const operator = isAddition ? '+' : '-'
let num1: Fraction
let num2: Fraction
let isSameDenominator: boolean
if (this.difficulty === 'same') {
isSameDenominator = true
} else if (this.difficulty === 'different') {
isSameDenominator = false
} else {
isSameDenominator = Math.random() > 0.5
}
if (isSameDenominator) {
const denom = this.randomInt(2, 10)
let num1Num = this.randomInt(1, denom - 1)
let num2Num = this.randomInt(1, denom - 1)
if (!isAddition && num1Num < num2Num) {
const temp = num1Num
num1Num = num2Num
num2Num = temp
}
num1 = { numerator: num1Num, denominator: denom }
num2 = { numerator: num2Num, denominator: denom }
} else {
const denom1 = this.randomInt(2, 8)
const denom2 = this.randomInt(2, 8)
let validDenom2 = denom2
while (validDenom2 === denom1 || this.gcd(denom1, validDenom2) === denom1 || this.gcd(denom1, validDenom2) === validDenom2) {
validDenom2 = this.randomInt(2, 8)
}
const num1Num = this.randomInt(1, denom1 - 1)
let num2Num = this.randomInt(1, validDenom2 - 1)
num1 = { numerator: num1Num, denominator: denom1 }
num2 = { numerator: num2Num, denominator: validDenom2 }
if (!isAddition) {
const result = this.calculateAnswer(num1, num2, operator)
if (result.numerator < 0) {
const temp = num1
num1 = num2
num2 = temp
}
}
}
const answer = this.calculateAnswer(num1, num2, operator)
const steps = this.generateSteps(num1, num2, operator, isSameDenominator)
this.currentQuestion = {
num1: num1,
num2: num2,
operator: operator,
isSameDenominator: isSameDenominator,
answer: answer,
steps: steps
}
this.userAnswer = ''
this.showSteps = false
this.showResult = false
this.currentStep = 0
if (!this.isTimerRunning) {
this.startTimer()
}
}
private randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min
}
private gcd(a: number, b: number): number {
a = Math.abs(a)
b = Math.abs(b)
while (b !== 0) {
const temp = b
b = a % b
a = temp
}
return a
}
private lcm(a: number, b: number): number {
return Math.abs(a * b) / this.gcd(a, b)
}
private calculateAnswer(num1: Fraction, num2: Fraction, operator: string): Fraction {
const commonDenom = this.lcm(num1.denominator, num2.denominator)
const factor1 = commonDenom / num1.denominator
const factor2 = commonDenom / num2.denominator
let resultNum: number
if (operator === '+') {
resultNum = num1.numerator * factor1 + num2.numerator * factor2
} else {
resultNum = num1.numerator * factor1 - num2.numerator * factor2
}
const divisor = this.gcd(Math.abs(resultNum), commonDenom)
return {
numerator: resultNum / divisor,
denominator: commonDenom / divisor
}
}
private generateSteps(num1: Fraction, num2: Fraction, operator: string, isSameDenominator: boolean): StepInfo[] {
const steps: StepInfo[] = []
if (isSameDenominator) {
let resultNum: number
if (operator === '+') {
resultNum = num1.numerator + num2.numerator
steps.push({
title: '同分母加法',
content: `分母不变,分子相加:${num1.numerator} + ${num2.numerator} = ${resultNum}`,
type: 'calculate'
})
} else {
resultNum = num1.numerator - num2.numerator
steps.push({
title: '同分母减法',
content: `分母不变,分子相减:${num1.numerator} - ${num2.numerator} = ${resultNum}`,
type: 'calculate'
})
}
const divisor = this.gcd(Math.abs(resultNum), num1.denominator)
if (divisor > 1) {
steps.push({
title: '约分',
content: `分子分母同时除以${divisor}:${resultNum}/${num1.denominator} = ${resultNum / divisor}/${num1.denominator / divisor}`,
type: 'simplify'
})
}
} else {
const commonDenom = this.lcm(num1.denominator, num2.denominator)
steps.push({
title: '找公分母',
content: `${num1.denominator}和${num2.denominator}的最小公倍数是${commonDenom}`,
type: 'find_lcm'
})
const factor1 = commonDenom / num1.denominator
const factor2 = commonDenom / num2.denominator
steps.push({
title: '通分',
content: `${num1.numerator}/${num1.denominator} = ${num1.numerator * factor1}/${commonDenom}\n${num2.numerator}/${num2.denominator} = ${num2.numerator * factor2}/${commonDenom}`,
type: 'convert'
})
let resultNum: number
if (operator === '+') {
resultNum = num1.numerator * factor1 + num2.numerator * factor2
steps.push({
title: '计算',
content: `${num1.numerator * factor1} + ${num2.numerator * factor2} = ${resultNum}`,
type: 'calculate'
})
} else {
resultNum = num1.numerator * factor1 - num2.numerator * factor2
steps.push({
title: '计算',
content: `${num1.numerator * factor1} - ${num2.numerator * factor2} = ${resultNum}`,
type: 'calculate'
})
}
const divisor = this.gcd(Math.abs(resultNum), commonDenom)
if (divisor > 1) {
steps.push({
title: '约分',
content: `分子分母同时除以${divisor}:${resultNum}/${commonDenom} = ${resultNum / divisor}/${commonDenom / divisor}`,
type: 'simplify'
})
}
}
return steps
}
private showStepByStep(): void {
this.showSteps = true
this.currentStep = 0
}
private nextStep(): void {
if (this.currentStep < this.currentQuestion!.steps.length - 1) {
this.currentStep++
}
}
private checkAnswer(): void {
if (!this.userAnswer.trim()) {
return
}
const parts = this.userAnswer.split('/')
if (parts.length !== 2) {
return
}
const userNum = parseInt(parts[0])
const userDenom = parseInt(parts[1])
if (isNaN(userNum) || isNaN(userDenom) || userDenom === 0) {
return
}
const divisor = this.gcd(Math.abs(userNum), userDenom)
const simplifiedNum = userNum / divisor
const simplifiedDenom = userDenom / divisor
this.isCorrect = simplifiedNum === this.currentQuestion!.answer.numerator &&
simplifiedDenom === this.currentQuestion!.answer.denominator
this.totalQuestions++
if (this.isCorrect) {
this.correctCount++
this.score += this.currentQuestion!.isSameDenominator ? 10 : 20
}
this.showResult = true
}
private nextQuestion(): void {
this.generateNewQuestion()
}
}
更多推荐



所有评论(0)