DEV Community

陈杨
陈杨

Posted on

Hong Mong 5 Development Treasure Case Sharing More Than One Breakpoint Development Practice

🌟
【HarmonyOS Advanced Development Practice】In-depth Analysis of Six Core Cases to Master Multi-terminal Adaptation!


📐 Case 4: Dynamic Grid Layout (E-commerce Product List)

Application Scenario: Product display differences on phone/tablet

Pain Point Analysis: Single column on phone → multi-column on tablet, need to intelligently calculate the number of items displayed

@Component
struct GoodsGrid {
  @StorageLink('currentWidthBreakpoint') bp: string

  build() {
    Grid() {
      ForEach(goodsList, (item) => {
        GridItem() {
          GoodsItem(item) // Product card component
        }
      })
    }
    .columnsTemplate(this.getColumnsTemplate())
    .rowsTemplate(this.getRowsTemplate())
  }

  // Dynamic column calculation
  private getColumnsTemplate(): string {
    switch(this.bp) {
      case 'sm': return '1fr';       // Single column on phone
      case 'md': return '1fr 1fr';   // Dual column on foldable
      case 'lg': return '1fr 1fr 1fr'; // Triple column on tablet
      default: return '1fr';
    }
  }

  // Dynamic row spacing
  private getRowsTemplate(): string {
    return this.bp === 'sm' ? '56vp' : '72vp'; // Larger spacing on large screens
  }
}
Enter fullscreen mode Exit fullscreen mode

Effect Comparison:

  • Phone (sm): Waterfall single column, compact spacing
  • Foldable (md): Dual column, adaptive images
  • Tablet (lg): Triple column, additional product parameters

🖥️ Case 5: Adaptive Navigation Bar (Cross-device Menu)

Application Scenario: Sidebar on Pad landscape, hamburger menu on phone

Key Technology: @Watch listener + conditional rendering

@Entry
struct MainPage {
  @State isCollapsed: boolean = false
  @StorageLink('currentWidthBreakpoint') bp: string

  // Automatically triggered on breakpoint change
  @Watch('bp')
  onBpChange() {
    this.isCollapsed = this.bp === 'sm'; // Auto collapse on small screen
  }

  build() {
    Row() {
      // Sidebar (shown on large screens)
      if(!this.isCollapsed) {
        Column() {
          MenuItems() // Navigation menu component
        }
        .width(240)
        .backgroundColor('#F5F5F5')
      }

      // Main content area
      Column() {
        Header({ 
          showMenuBtn: this.isCollapsed // Show menu button on small screen
        })
        Content()
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Interaction Logic:

  • Width ≥ 840vp (lg): Fixed 240vp left navigation bar
  • 320vp ≤ Width < 840vp (md): Collapsed as floating sidebar
  • Width < 320vp (xs): Hide navigation, show hamburger menu at top

📖 Case 6: Reader Split Mode (Document App)

Special Requirement: Special layout for half-open foldable

Core Code:

@Component
struct ReaderLayout {
  @StorageLink('currentHeightBreakpoint') hBp: string

  build() {
    Flex({ direction: FlexDirection.Row }) {
      // Left catalog (show when aspect ratio > 1.2)
      if(this.hBp === 'lg') {
        Column() {
          ChapterList() // Catalog component
        }
        .width('30%')
      }

      // Main reading area
      Scroll() {
        TextContent() // Text component
      }
      .flexGrow(1)

      // Right notes (shown on square screen)
      if(this.hBp === 'md') {
        Column() {
          NotesPanel() // Notes panel
        }
        .width(280)
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Device Adaptation:

  • Phone portrait (hBp=lg): Single column reading, floating catalog at bottom
  • Foldable half-open (hBp=md): Main text + right notes dual column
  • Tablet landscape (hBp=sm): Three columns (catalog + text + annotation)

🎥 Case 7: Video Player Multi-form Adaptation

Complex Scenario: Fullscreen/split-screen/picture-in-picture mode

Key Technology Points: Window state monitoring + dynamic styles

@Component
struct VideoPlayer {
  @StorageLink('currentWidthBreakpoint') bp: string
  @State isFullscreen: boolean = false

  build() {
    Stack() {
      VideoComponent() // Core player
        .aspectRatio(this.getVideoRatio()) // Dynamic ratio

      // Fullscreen mode controls
      if(this.isFullscreen) {
        FullscreenControls()
      }
    }
    .onClick(() => {
      if(this.bp === 'sm') { // Tap to toggle fullscreen on small screen
        this.toggleFullscreen();
      }
    })
  }

  // Set video ratio by breakpoint
  private getVideoRatio(): number {
    switch(this.bp) {
      case 'sm': return 16/9;  // 16:9 on phone
      case 'md': return 21/9;  // 21:9 on foldable
      case 'lg': return this.isFullscreen ? 16/9 : 4/3; // Tablet adaptation
      default: return 16/9;
    }
  }

  private toggleFullscreen() {
    // Fullscreen logic (omitted)
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Adaptation Strategies:

  1. Phone portrait: Default 16:9, tap to fullscreen
  2. Foldable unfolded: 21:9 cinema ratio
  3. Tablet split-screen: 4:3 for multitasking layout

📊 Case 8: Dynamic Layout for Data Dashboard

Business Requirement: Multi-terminal data visualization from smart watch to smart screen

Code Snippet:

@Entry
struct Dashboard {
  @StorageLink('currentWidthBreakpoint') bp: string

  build() {
    GridRow({ columns: this.getGridColumns() }) {
      GridCol({ span: { xs:12, sm:6, md:4, lg:3 } }) {
        DataCard('Sales', '¥12.34M') // Data card
      }

      GridCol({ span: { xs:12, sm:6, md:8, lg:6 } }) {
        ChartComponent() // Visualization chart
      }
    }
  }

  private getGridColumns(): number | GridRowColumnOption {
    return {
      xs: 4,   // 4 columns for watch (80vp each)
      sm: 8,   // 8 columns for phone
      md: 12,  // 12 columns for foldable
      lg: 24   // 24 columns for large screen
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Layout Strategy:

  • Watch (xs): Key metrics stacked in single column
  • Phone (sm): Left-right block display
  • Smart screen (lg): 24-column fine layout, multi-chart linkage

🛠️ Case 9: Automatic Form Layout Optimization

Typical Scenario: Login screen portrait/landscape adaptation

Innovative Solution: Mixed grid + flex layout

@Component
struct LoginForm {
  @StorageLink('currentHeightBreakpoint') hBp: string

  build() {
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center }) {
      Image($r('app.media.logo'))
        .height(this.hBp === 'sm' ? 80 : 120) // Shrink logo in landscape

      FormLayout() {
        TextInput().placeholder('Account')
        TextInput().placeholder('Password')
      }
      .layoutType(this.hBp === 'sm' ? LayoutType.Grid : LayoutType.List)

      Button('Login')
        .width(this.hBp === 'sm' ? '60%' : '40%')
    }
    .padding(this.getFormPadding())
  }

  private getFormPadding(): Padding {
    return {
      top: this.hBp === 'lg' ? 40 : 20, // More top padding in portrait
      bottom: 20,
      left: this.hBp === 'sm' ? 10 : 30, // Compact layout in landscape
      right: this.hBp === 'sm' ? 10 : 30
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Layout Changes:

  • Portrait (hBp=lg): List arrangement, large spacing
  • Landscape (hBp=sm): Compact grid layout, adaptive width

💡 Developer Tips (Pitfall Avoidance Guide)

  1. Breakpoint Coverage Strategy
// Wrong: Missed breakpoint range
if(bp === 'sm') {...} 
else {...}

// Correct: Full coverage
switch(bp) {
  case 'xs':... 
  case 'sm':...
  case 'md':...
  case 'lg':...
  default:...
}
Enter fullscreen mode Exit fullscreen mode
  1. Golden Rule for Unit Selection
  2. Text: fp (font pixel, auto adapts to system scaling)
  3. Layout: vp (virtual pixel, screen density independent)
  4. Media resources: px (physical pixel, ensures clarity)
  5. Multi-device Debugging Tips
# Start multiple devices at once via command line
hdc shell snapshot_demo -s 1080x2340 -d "Phone"
hdc shell snapshot_demo -s 2200x2480 -d "Foldable"
Enter fullscreen mode Exit fullscreen mode

🌐 Ultimate Adaptation Architecture

├── resources
│   ├── breakpoints
│   │   ├── phone.ets    # Phone-specific layout
│   │   ├── tablet.ets   # Tablet layout strategy
│   │   └── foldable.ets # Foldable adaptation
├── utils
│   ├── BreakpointManager.ets # Breakpoint state management
│   └── LayoutCalculator.ets # Dynamic layout calculation
└── components
    ├── AdaptiveContainer.ets # Adaptive container
    └── ResponsiveImage.ets   # Responsive image component
Enter fullscreen mode Exit fullscreen mode

After mastering these cases, you will be able to flexibly combine these patterns like building blocks when facing complex multi-terminal adaptation needs. If you encounter particularly tricky adaptation problems, feel free to leave a comment and let's discuss solutions together! 🚀

Top comments (0)