HarmonyOS NEXT Development Case: Garbage Classification
zhongcx

zhongcx @zhongcx

About: The best time to plant a tree was 10 years ago, the second-best time is now.

Joined:
Mar 6, 2025

HarmonyOS NEXT Development Case: Garbage Classification

Publish Date: May 11
0 0

Image description

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
};
Enter fullscreen mode Exit fullscreen mode

Technical Highlights

  1. 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.

  2. Dynamic Shuffling:

    Implements Fisher-Yates algorithm for randomizing question order, ensuring varied quiz experiences.

  3. Particle Animation:

    Creates engaging visual feedback using HarmonyOS's Particle system with configurable:

    • Emission rates
    • Particle trajectories
    • Color transitions
    • Scaling effects
  4. Gesture Interactions:

    Features spring-based animations with curves.springCurve for smooth category selection feedback.

  5. Responsive Layout:

    Implements adaptive UI using:

    • Percentage-based dimensions
    • Flexbox layout (Row/Column)
    • Stack positioning
    • Dynamic translation transforms
  6. Result Visualization:

    Uses ActionSheet component to display detailed score breakdown with scrollable history.

Development Considerations

  1. Performance Optimization:

    • Animation frame synchronization
    • Object pooling for particle effects
    • Memoization of static resources
  2. Localization Support:

    The architecture supports easy translation through centralized string management in class definitions.

  3. 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.

Comments 0 total

    Add comment