HarmonyOS NEXT Development Case: Garbage Classification
import { curves } from '@kit.ArkUI'; // Import curve module from ArkUI toolkit // Define Garbage Item Class class GarbageItem { name: string; // Item name type: number; // Category type description?: string; // Optional description // Constructor for initializing garbage item constructor(name: string, type: number, description?: string) { this.name = name; // Set item name this.type = type; // Set category type this.description = description || ""; // Set description with default empty string } } // Define Garbage Category Class class GarbageCategory { name: string; // Category name type: number; // Category type color: string; // Display color description: string; // Category description // Constructor for initializing garbage category constructor(name: string, type: number, color: string, description: string) { this.name = name; // Set category name this.type = type; // Set category type this.color = color; // Set display color this.description = description; // Set category description } } // Application entry component with decorators @Entry @Component struct Index { // State management variables @State currentQuestionIndex: number = 0; // Current question index @State quizResults: string[] = []; // Quiz result records @State totalScore: number = 0; // Total score @State showAnimation: boolean = false; // Animation control flag @State scaleOptions: ScaleOptions = { x: 1, y: 1 }; // Scaling parameters @State itemXPosition: number = 0; // Item X-axis position @State itemOpacity: number = 1.0; // Item opacity // Predefined garbage categories @State garbageCategories: GarbageCategory[] = [ new GarbageCategory("Hazardous", 0, "#e2413f", "Waste harmful to human health or environment"), new GarbageCategory("Recyclable", 1, "#1c6bb5", "Reusable and recyclable materials"), new GarbageCategory("Kitchen", 2, "#4ca84e", "Biodegradable organic waste"), new GarbageCategory("Other", 3, "#5f5f5f", "Non-classifiable residual waste"), ]; // Predefined garbage items @State garbageItems: GarbageItem[] = [ // Kitchen waste examples new GarbageItem("Vegetable leaves", 2), new GarbageItem("Leftovers", 2), // ... (other items remain similar with English translations) new GarbageItem("Toilet paper", 3, "Water-soluble paper products cannot be recycled") ]; // Lifecycle hook for initializing quiz aboutToAppear(): void { this.resetQuiz(); } // Quiz initialization logic resetQuiz() { this.quizResults = []; this.totalScore = 0; this.currentQuestionIndex = 0; this.shuffleItems(); } // Fisher-Yates shuffle algorithm implementation shuffleItems() { for (let i = this.garbageItems.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [this.garbageItems[i], this.garbageItems[j]] = [this.garbageItems[j], this.garbageItems[i]]; } } // Answer validation logic checkAnswer(categoryType: number) { const currentItem = this.garbageItems[this.currentQuestionIndex]; const isCorrect = currentItem.type === categoryType; this.quizResults.push( `${currentItem.name} (${this.garbageCategories[categoryType].name}) ` + `[${isCorrect ? "✓" : this.garbageCategories[currentItem.type].name}]` ); if (isCorrect) this.totalScore += 10; this.currentQuestionIndex++; if (this.currentQuestionIndex >= 10) { this.displayResults(); this.resetQuiz(); } } // Results display implementation displayResults() { const resultSheets = this.quizResults.map(result => ({ title: result, action: () => {} })); this.getUIContext().showActionSheet({ title: 'Score Report', message: `Total Score: ${this.totalScore}`, confirm: { defaultFocus: true, value: 'OK', action: () => {} }, alignment: DialogAlignment.Center, sheets: resultSheets }); } // UI construction using ArkUI declarative syntax build() { Column() { Text(`Question: ${this.currentQuestionIndex + 1}/10`) .fontSize(30) .margin(20); Stack() { // Animated item display Text(this.garbageItems[this.currentQuestionIndex].name) .size(130) .style({ border: { width: 1 }, borderRadius: 5, backgroundColor: Color.Orange, textAlign: TextAlign.Center, fontSize: 20, padding: 2 }) .scale(this.scaleOptions); // Particle animation implementation if (this.showAnimation) { Particle({ particles: [{ emitter: { particle: { type: ParticleType.POINT, config: { radius: 5 }}, emitRate: 100, position: ['25%', 0], shape: ParticleEmitterShape.RECTANGLE }, // ... (particle configuration remai

import { curves } from '@kit.ArkUI'; // Import curve module from ArkUI toolkit
// Define Garbage Item Class
class GarbageItem {
name: string; // Item name
type: number; // Category type
description?: string; // Optional description
// Constructor for initializing garbage item
constructor(name: string, type: number, description?: string) {
this.name = name; // Set item name
this.type = type; // Set category type
this.description = description || ""; // Set description with default empty string
}
}
// Define Garbage Category Class
class GarbageCategory {
name: string; // Category name
type: number; // Category type
color: string; // Display color
description: string; // Category description
// Constructor for initializing garbage category
constructor(name: string, type: number, color: string, description: string) {
this.name = name; // Set category name
this.type = type; // Set category type
this.color = color; // Set display color
this.description = description; // Set category description
}
}
// Application entry component with decorators
@Entry
@Component
struct Index {
// State management variables
@State currentQuestionIndex: number = 0; // Current question index
@State quizResults: string[] = []; // Quiz result records
@State totalScore: number = 0; // Total score
@State showAnimation: boolean = false; // Animation control flag
@State scaleOptions: ScaleOptions = { x: 1, y: 1 }; // Scaling parameters
@State itemXPosition: number = 0; // Item X-axis position
@State itemOpacity: number = 1.0; // Item opacity
// Predefined garbage categories
@State garbageCategories: GarbageCategory[] = [
new GarbageCategory("Hazardous", 0, "#e2413f", "Waste harmful to human health or environment"),
new GarbageCategory("Recyclable", 1, "#1c6bb5", "Reusable and recyclable materials"),
new GarbageCategory("Kitchen", 2, "#4ca84e", "Biodegradable organic waste"),
new GarbageCategory("Other", 3, "#5f5f5f", "Non-classifiable residual waste"),
];
// Predefined garbage items
@State garbageItems: GarbageItem[] = [
// Kitchen waste examples
new GarbageItem("Vegetable leaves", 2),
new GarbageItem("Leftovers", 2),
// ... (other items remain similar with English translations)
new GarbageItem("Toilet paper", 3, "Water-soluble paper products cannot be recycled")
];
// Lifecycle hook for initializing quiz
aboutToAppear(): void {
this.resetQuiz();
}
// Quiz initialization logic
resetQuiz() {
this.quizResults = [];
this.totalScore = 0;
this.currentQuestionIndex = 0;
this.shuffleItems();
}
// Fisher-Yates shuffle algorithm implementation
shuffleItems() {
for (let i = this.garbageItems.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.garbageItems[i], this.garbageItems[j]] = [this.garbageItems[j], this.garbageItems[i]];
}
}
// Answer validation logic
checkAnswer(categoryType: number) {
const currentItem = this.garbageItems[this.currentQuestionIndex];
const isCorrect = currentItem.type === categoryType;
this.quizResults.push(
`${currentItem.name} (${this.garbageCategories[categoryType].name}) ` +
`[${isCorrect ? "✓" : this.garbageCategories[currentItem.type].name}]`
);
if (isCorrect) this.totalScore += 10;
this.currentQuestionIndex++;
if (this.currentQuestionIndex >= 10) {
this.displayResults();
this.resetQuiz();
}
}
// Results display implementation
displayResults() {
const resultSheets = this.quizResults.map(result => ({
title: result,
action: () => {}
}));
this.getUIContext().showActionSheet({
title: 'Score Report',
message: `Total Score: ${this.totalScore}`,
confirm: {
defaultFocus: true,
value: 'OK',
action: () => {}
},
alignment: DialogAlignment.Center,
sheets: resultSheets
});
}
// UI construction using ArkUI declarative syntax
build() {
Column() {
Text(`Question: ${this.currentQuestionIndex + 1}/10`)
.fontSize(30)
.margin(20);
Stack() {
// Animated item display
Text(this.garbageItems[this.currentQuestionIndex].name)
.size(130)
.style({
border: { width: 1 },
borderRadius: 5,
backgroundColor: Color.Orange,
textAlign: TextAlign.Center,
fontSize: 20,
padding: 2
})
.scale(this.scaleOptions);
// Particle animation implementation
if (this.showAnimation) {
Particle({
particles: [{
emitter: {
particle: { type: ParticleType.POINT, config: { radius: 5 }},
emitRate: 100,
position: ['25%', 0],
shape: ParticleEmitterShape.RECTANGLE
},
// ... (particle configuration remains similar)
}]
}).size('100%');
}
}
.size(150, 300)
.translate({ x: `${this.itemXPosition}px` });
// Category selection buttons
Row() {
ForEach(this.garbageCategories, (category) => {
Column() {
Text(category.name).style(categoryTextStyle);
Divider();
Text(category.description).style(descTextStyle);
}
.clickEffect({ scale: 0.8 })
.onClick(() => this.handleCategoryClick(category))
});
}
Button('Restart')
.onClick(() => this.resetQuiz())
.margin(50);
}
.width('100%');
}
// Animation handling logic
private handleCategoryClick(category: GarbageCategory) {
if (this.showAnimation) return;
this.showAnimation = true;
const positions = [-270, -90, 90, 270];
animateToImmediately({
duration: 200,
onFinish: this.handleAnimationComplete.bind(this, category)
}, () => {
this.itemXPosition = positions[category.type];
});
}
private handleAnimationComplete(category: GarbageCategory) {
animateToImmediately({
duration: 800,
curve: curves.springCurve(0, 20, 90, 20),
onFinish: this.finalizeAnswerCheck.bind(this, category)
}, () => {
this.scaleOptions = { x: 1.3, y: 1.3 };
});
}
private finalizeAnswerCheck(category: GarbageCategory) {
animateToImmediately({
duration: 200,
onFinish: () => {
this.resetAnimationState();
this.checkAnswer(category.type);
}
}, () => {
this.itemXPosition = 0;
this.scaleOptions = { x: 1.0, y: 1.0 };
});
}
private resetAnimationState() {
this.itemXPosition = 0;
this.showAnimation = false;
}
}
// Style constants
const categoryTextStyle = {
fontSize: 30,
color: Color.White,
padding: 5
};
const descTextStyle = {
fontSize: 28,
color: Color.White,
padding: 5
};
Technical Highlights
State Management:
Utilizes ArkUI's @State decorator for reactive UI updates. The component maintains multiple state variables including current question index, user score, and animation parameters.Dynamic Shuffling:
Implements Fisher-Yates algorithm for randomizing question order, ensuring varied quiz experiences.-
Particle Animation:
Creates engaging visual feedback using HarmonyOS's Particle system with configurable:- Emission rates
- Particle trajectories
- Color transitions
- Scaling effects
Gesture Interactions:
Features spring-based animations withcurves.springCurve
for smooth category selection feedback.-
Responsive Layout:
Implements adaptive UI using:- Percentage-based dimensions
- Flexbox layout (Row/Column)
- Stack positioning
- Dynamic translation transforms
Result Visualization:
Uses ActionSheet component to display detailed score breakdown with scrollable history.
Development Considerations
-
Performance Optimization:
- Animation frame synchronization
- Object pooling for particle effects
- Memoization of static resources
Localization Support:
The architecture supports easy translation through centralized string management in class definitions.Accessibility:
Built-in contrast ratios and click effect visual feedback enhance usability.
This implementation demonstrates HarmonyOS's capabilities in building engaging, interactive applications while maintaining clean code organization and smooth performance.