游戏主界面是玩家与数独游戏交互的核心场所。一个好的主界面需要将棋盘、控制按钮、数字键盘、信息栏等元素有机地组合在一起,同时还要处理计时器、暂停、胜利等游戏状态。今天我们来详细实现数独游戏的主界面。
请添加图片描述

在设计主界面之前,我们需要考虑几个关键问题。首先是布局结构,如何在有限的屏幕空间内合理安排各个组件。其次是状态管理,主界面需要响应多种游戏状态的变化。最后是生命周期管理,计时器的启动和停止需要与页面的生命周期同步。

让我们从创建GamePage组件开始。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../controllers/game_controller.dart';
import 'widgets/sudoku_board.dart';
import 'widgets/number_pad.dart';
import 'widgets/game_controls.dart';

这是游戏主界面的导入部分。dart:async用于Timer类,Material库提供UI组件,GetX用于状态管理,ScreenUtil用于屏幕适配。我们还导入了GameController和三个子组件:棋盘、数字键盘、游戏控制按钮。这种模块化的设计让代码结构清晰,每个组件负责自己的职责。

定义GamePage类和状态类。

class GamePage extends StatefulWidget {
  const GamePage({super.key});

  
  State<GamePage> createState() => _GamePageState();
}

class _GamePageState extends State<GamePage> {
  final GameController controller = Get.put(GameController());
  Timer? _timer;

GamePage使用StatefulWidget是因为需要管理计时器的生命周期。Get.put将GameController注册到GetX的依赖注入系统中,这样其他组件可以通过Get.find获取同一个实例。_timer用于存储计时器引用,方便后续取消。使用可空类型是因为计时器可能还没有创建。

初始化方法中启动计时器。

  
  void initState() {
    super.initState();
    _startTimer();
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      controller.incrementTimer();
    });
  }

initState在组件创建时调用,我们在这里启动计时器。Timer.periodic创建一个周期性定时器,每秒执行一次回调。回调中调用controller的incrementTimer方法来更新游戏时间。这种设计将计时逻辑与UI分离,计时器只负责触发更新,具体的时间管理由controller处理。

资源释放非常重要。

  
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

dispose在组件销毁时调用,我们在这里取消计时器。如果不取消,计时器会继续运行,导致内存泄漏和潜在的错误。使用?.操作符是因为_timer可能为null。这是Flutter开发中的基本规范,任何需要手动释放的资源都应该在dispose中处理。

新游戏对话框的实现。

  void _showNewGameDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('新游戏'),
        content: const Text('选择难度'),
        actions: [
          TextButton(
            onPressed: () {
              controller.generateNewGame('Easy');
              Navigator.pop(context);
            },
            child: const Text('简单'),
          ),

showDialog显示一个模态对话框,让玩家选择新游戏的难度。AlertDialog是Material Design的标准对话框组件,包含标题、内容和操作按钮。每个难度按钮点击后调用controller的generateNewGame方法生成新谜题,然后关闭对话框。这种设计让难度选择变得直观简单。

继续添加其他难度选项。

          TextButton(
            onPressed: () {
              controller.generateNewGame('Medium');
              Navigator.pop(context);
            },
            child: const Text('中等'),
          ),
          TextButton(
            onPressed: () {
              controller.generateNewGame('Hard');
              Navigator.pop(context);
            },
            child: const Text('困难'),
          ),
          TextButton(
            onPressed: () {
              controller.generateNewGame('Expert');
              Navigator.pop(context);
            },
            child: const Text('专家'),
          ),
        ],
      ),
    );
  }

四个难度级别对应不同数量的初始数字:简单约43个、中等约33个、困难约28个、专家约23个。初始数字越少,谜题越难。TextButton是Material Design的文字按钮,适合在对话框中使用。Navigator.pop关闭当前对话框,返回游戏界面。

胜利对话框的实现。

  void _showVictoryDialog() {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (context) => AlertDialog(
        title: const Text('🎉 恭喜!'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text('你完成了数独!'),
            SizedBox(height: 10.h),
            Text('用时: ${controller.formattedTime}'),
            Text('提示次数: ${controller.hintsUsed}'),
          ],
        ),

胜利对话框在玩家完成数独时显示。barrierDismissible设为false防止玩家点击对话框外部关闭它,确保玩家看到自己的成绩。对话框内容包括祝贺文字、用时和提示次数。mainAxisSize设为min让Column只占用必要的空间。这些信息让玩家有成就感,也方便他们追踪自己的进步。

胜利对话框的操作按钮。

        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _showNewGameDialog();
            },
            child: const Text('新游戏'),
          ),
        ],
      ),
    );
  }

胜利后只提供一个"新游戏"按钮,点击后先关闭胜利对话框,再显示新游戏对话框让玩家选择难度。这种流程设计让玩家可以快速开始下一局游戏。如果需要,还可以添加"分享成绩"、"查看统计"等按钮。

主界面的build方法。

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('数独'),
        actions: [
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: _showNewGameDialog,
          ),
        ],
      ),

Scaffold提供了Material Design的基本页面结构,包括AppBar和body。AppBar显示标题"数独",右侧有一个加号按钮用于开始新游戏。使用Icons.add图标直观地表示"新建"的含义。这种布局是移动应用的常见模式,用户很容易理解。

根据游戏状态显示不同的内容。

      body: GetBuilder<GameController>(
        builder: (ctrl) {
          if (ctrl.isComplete) {
            WidgetsBinding.instance.addPostFrameCallback((_) {
              _showVictoryDialog();
            });
          }
          
          return ctrl.isPaused
              ? _buildPausedScreen()
              : _buildGameScreen();
        },
      ),
    );
  }

GetBuilder监听GameController的状态变化。当isComplete为true时,使用addPostFrameCallback在当前帧渲染完成后显示胜利对话框,避免在build过程中调用showDialog导致的错误。根据isPaused状态显示暂停界面或游戏界面。这种条件渲染让界面能够响应各种游戏状态。

暂停界面的实现。

  Widget _buildPausedScreen() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.pause_circle_outline, size: 80.sp, color: Colors.grey),
          SizedBox(height: 20.h),
          Text('游戏已暂停', style: TextStyle(fontSize: 24.sp)),
          SizedBox(height: 20.h),
          ElevatedButton(
            onPressed: controller.resumeGame,
            child: const Text('继续游戏'),
          ),
        ],
      ),
    );
  }

暂停界面显示一个大的暂停图标、提示文字和继续按钮。Center和Column组合实现垂直居中布局。图标使用80sp的大尺寸,让暂停状态一目了然。ElevatedButton是Material Design的凸起按钮,点击后调用controller的resumeGame方法恢复游戏。暂停时隐藏棋盘可以防止玩家在暂停时继续思考。

游戏界面的实现。

  Widget _buildGameScreen() {
    return SafeArea(
      child: Padding(
        padding: EdgeInsets.all(16.w),
        child: Column(
          children: [
            _buildInfoBar(),
            SizedBox(height: 16.h),
            const Expanded(child: SudokuBoard()),
            SizedBox(height: 16.h),
            const GameControls(),
            SizedBox(height: 16.h),
            const NumberPad(),
          ],
        ),
      ),
    );
  }

SafeArea确保内容不会被系统UI遮挡,比如刘海屏或底部导航条。Padding添加16像素的内边距,让内容与屏幕边缘保持距离。Column垂直排列各个组件:信息栏、棋盘、控制按钮、数字键盘。棋盘使用Expanded占据剩余空间,确保它尽可能大。各组件之间有16像素的间距,让布局更加舒适。

信息栏的实现。

  Widget _buildInfoBar() {
    return GetBuilder<GameController>(
      builder: (ctrl) => Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Container(
            padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
            decoration: BoxDecoration(
              color: Colors.blue.shade100,
              borderRadius: BorderRadius.circular(16.r),
            ),
            child: Text(
              ctrl.difficulty,
              style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold),
            ),
          ),

信息栏使用Row水平排列三个元素:难度标签、计时器、暂停按钮。难度标签使用圆角容器包裹,蓝色背景让它更加醒目。GetBuilder确保当难度或时间变化时UI会更新。mainAxisAlignment设为spaceBetween让三个元素分布在两端和中间。

计时器和暂停按钮。

          Row(
            children: [
              Icon(Icons.timer, size: 20.sp),
              SizedBox(width: 4.w),
              Text(
                ctrl.formattedTime,
                style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
              ),
            ],
          ),
          IconButton(
            icon: const Icon(Icons.pause),
            onPressed: ctrl.pauseGame,
          ),
        ],
      ),
    );
  }
}

计时器显示一个时钟图标和格式化的时间。formattedTime是controller中的计算属性,返回"MM:SS"格式的字符串。暂停按钮使用IconButton,点击后调用pauseGame方法。这三个元素提供了游戏的关键信息和控制,让玩家随时了解游戏状态。

现在让我们看看GameController中相关方法的实现。

  void pauseGame() {
    isPaused = true;
    update();
  }

  void resumeGame() {
    isPaused = false;
    update();
  }

暂停和恢复方法非常简单,只需要切换isPaused状态并通知UI更新。当isPaused为true时,计时器的incrementTimer方法不会增加时间,游戏界面也会切换到暂停状态。这种设计将状态逻辑集中在controller中,UI只负责显示。

计时器增量方法。

  void incrementTimer() {
    if (!isPaused && !isComplete) {
      elapsedSeconds++;
      update();
    }
  }

  String get formattedTime {
    int minutes = elapsedSeconds ~/ 60;
    int seconds = elapsedSeconds % 60;
    return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
  }

incrementTimer只在游戏进行中才增加时间,暂停或完成时不计时。formattedTime是一个getter,将秒数转换为"MM:SS"格式。padLeft确保分钟和秒数都是两位数,比如"05:09"而不是"5:9"。这种格式化让时间显示更加规范美观。

游戏完成检测。

  void _checkCompletion() {
    for (int i = 0; i < 9; i++) {
      for (int j = 0; j < 9; j++) {
        if (board[i][j] == 0 || board[i][j] != solution[i][j]) {
          return;
        }
      }
    }
    isComplete = true;
    update();
  }

_checkCompletion遍历整个棋盘,检查每个单元格是否都填入了正确的数字。如果有任何空格或错误,直接返回。只有当所有单元格都正确时,才将isComplete设为true。这个方法在每次输入数字或使用提示后调用,确保及时检测到游戏完成。

让我们来优化主界面的用户体验。添加页面切换动画。

class AnimatedGamePage extends StatefulWidget {
  const AnimatedGamePage({super.key});

  
  State<AnimatedGamePage> createState() => _AnimatedGamePageState();
}

class _AnimatedGamePageState extends State<AnimatedGamePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _fadeAnimation;
  
  
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(parent: _animationController, curve: Curves.easeIn),
    );
    _animationController.forward();
  }

添加淡入动画让页面切换更加平滑。AnimationController控制动画时长为300毫秒,_fadeAnimation定义从0到1的透明度变化。在initState中启动动画,页面会从透明渐变到完全显示。这种动画效果提升了应用的质感。

在build中应用动画。

  
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _fadeAnimation,
      child: Scaffold(
        // 页面内容
      ),
    );
  }
  
  
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
}

FadeTransition将动画应用到整个页面。dispose中释放AnimationController避免内存泄漏。这种简单的动画可以显著提升用户体验,让应用感觉更加流畅专业。

处理返回键的行为。

  
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (controller.isPaused) {
          controller.resumeGame();
          return false;
        }
        return await _showExitConfirmDialog() ?? false;
      },
      child: Scaffold(
        // 页面内容
      ),
    );
  }
  
  Future<bool?> _showExitConfirmDialog() {
    return showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('退出游戏'),
        content: const Text('确定要退出吗?当前进度将会丢失。'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('确定'),
          ),
        ],
      ),
    );
  }

WillPopScope拦截返回键事件。如果游戏处于暂停状态,按返回键会恢复游戏而不是退出。如果游戏进行中,会显示确认对话框,防止玩家误操作丢失进度。这种设计考虑到了用户的实际使用场景,提供了更好的体验。

添加键盘快捷键支持。

  
  Widget build(BuildContext context) {
    return RawKeyboardListener(
      focusNode: FocusNode(),
      autofocus: true,
      onKey: (event) {
        if (event is RawKeyDownEvent) {
          final key = event.logicalKey;
          if (key.keyId >= 49 && key.keyId <= 57) {
            // 数字键1-9
            controller.enterNumber(key.keyId - 48);
          } else if (key == LogicalKeyboardKey.backspace) {
            controller.eraseCell();
          } else if (key == LogicalKeyboardKey.keyZ && 
                     event.isControlPressed) {
            controller.undoMove();
          }
        }
      },
      child: Scaffold(
        // 页面内容
      ),
    );
  }

RawKeyboardListener监听键盘事件,支持数字键输入、退格键擦除、Ctrl+Z撤销。这对于在平板或桌面设备上使用外接键盘的用户非常有用。autofocus确保页面加载后立即获得焦点,可以接收键盘输入。

主界面的响应式布局。

  Widget _buildResponsiveLayout() {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 600) {
          // 平板或桌面布局
          return _buildWideLayout();
        } else {
          // 手机布局
          return _buildNarrowLayout();
        }
      },
    );
  }
  
  Widget _buildWideLayout() {
    return Row(
      children: [
        Expanded(
          flex: 2,
          child: Column(
            children: [
              _buildInfoBar(),
              const Expanded(child: SudokuBoard()),
            ],
          ),
        ),
        Expanded(
          flex: 1,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const GameControls(),
              SizedBox(height: 24.h),
              const NumberPad(),
            ],
          ),
        ),
      ],
    );
  }

LayoutBuilder根据可用宽度选择不同的布局。宽屏设备使用左右布局,棋盘在左边占2/3,控制区在右边占1/3。窄屏设备使用上下布局,棋盘在上,控制区在下。这种响应式设计让应用在不同设备上都有良好的体验。

主界面的主题适配。

  Widget build(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    
    return Scaffold(
      backgroundColor: isDark ? Colors.grey.shade900 : Colors.grey.shade100,
      appBar: AppBar(
        backgroundColor: isDark ? Colors.grey.shade800 : Colors.white,
        foregroundColor: isDark ? Colors.white : Colors.black,
        title: const Text('数独'),
        actions: [
          IconButton(
            icon: const Icon(Icons.add),
            onPressed: _showNewGameDialog,
          ),
        ],
      ),
      body: _buildGameScreen(),
    );
  }

通过Theme.of获取当前主题的亮度,根据是否为深色模式设置不同的背景色和前景色。这种适配让应用能够跟随系统主题变化,在深色模式下也有良好的显示效果。

总结一下游戏主界面的关键设计要点。首先是清晰的布局结构,将信息栏、棋盘、控制区合理排列。其次是完善的状态管理,响应暂停、完成等游戏状态。然后是生命周期管理,正确处理计时器的启动和停止。最后是用户体验优化,包括动画、返回键处理、键盘支持等。

游戏主界面是整个数独应用的核心,它将各个组件整合在一起,提供完整的游戏体验。通过合理的布局和状态管理,玩家可以专注于解题本身,享受数独带来的乐趣。

在实际开发中,我们还需要处理一些边界情况和用户体验细节。比如应用进入后台时的处理。

class _GamePageState extends State<GamePage> with WidgetsBindingObserver {
  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _startTimer();
  }
  
  
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _timer?.cancel();
    super.dispose();
  }
  
  
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      controller.pauseGame();
    }
  }
}

WidgetsBindingObserver可以监听应用的生命周期变化。当应用进入后台时,自动暂停游戏,防止计时器继续运行。这是一个重要的用户体验细节,确保计时的公平性。

游戏进度的自动保存功能也很重要。

void _autoSave() {
  final gameState = {
    'board': controller.board,
    'solution': controller.solution,
    'isFixed': controller.isFixed,
    'notes': controller.notes.map((row) => 
        row.map((set) => set.toList()).toList()).toList(),
    'difficulty': controller.difficulty,
    'elapsedSeconds': controller.elapsedSeconds,
    'hintsUsed': controller.hintsUsed,
  };
  
  SharedPreferences.getInstance().then((prefs) {
    prefs.setString('savedGame', jsonEncode(gameState));
  });
}

_autoSave将当前游戏状态序列化为JSON并保存到本地存储。notes需要特殊处理,因为Set不能直接序列化。这个方法可以在每次操作后调用,或者使用定时器定期保存。

恢复保存的游戏进度。

Future<bool> _loadSavedGame() async {
  final prefs = await SharedPreferences.getInstance();
  final savedGame = prefs.getString('savedGame');
  
  if (savedGame == null) return false;
  
  try {
    final gameState = jsonDecode(savedGame) as Map<String, dynamic>;
    
    controller.board = (gameState['board'] as List)
        .map((row) => (row as List).cast<int>().toList())
        .toList();
    controller.solution = (gameState['solution'] as List)
        .map((row) => (row as List).cast<int>().toList())
        .toList();
    controller.difficulty = gameState['difficulty'];
    controller.elapsedSeconds = gameState['elapsedSeconds'];
    controller.hintsUsed = gameState['hintsUsed'];
    
    controller.update();
    return true;
  } catch (e) {
    return false;
  }
}

_loadSavedGame从本地存储读取保存的游戏状态并恢复。使用try-catch处理可能的解析错误,确保应用不会因为损坏的数据而崩溃。返回值表示是否成功恢复。

启动时询问是否继续上次的游戏。

void _checkSavedGame() async {
  bool hasSavedGame = await _loadSavedGame();
  
  if (hasSavedGame && mounted) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('继续游戏'),
        content: const Text('发现未完成的游戏,是否继续?'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              controller.generateNewGame('Easy');
            },
            child: const Text('新游戏'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('继续'),
          ),
        ],
      ),
    );
  }
}

在initState中调用_checkSavedGame,如果有保存的游戏就显示对话框让用户选择。mounted检查确保widget还在树中,避免在dispose后调用showDialog。

游戏的音效反馈可以增强沉浸感。

class GameSoundManager {
  static final AudioPlayer _player = AudioPlayer();
  
  static Future<void> playTap() async {
    await _player.play(AssetSource('sounds/tap.mp3'));
  }
  
  static Future<void> playError() async {
    await _player.play(AssetSource('sounds/error.mp3'));
  }
  
  static Future<void> playSuccess() async {
    await _player.play(AssetSource('sounds/success.mp3'));
  }
  
  static Future<void> playVictory() async {
    await _player.play(AssetSource('sounds/victory.mp3'));
  }
}

GameSoundManager管理游戏中的各种音效。tap用于普通点击,error用于填入冲突数字,success用于正确填入,victory用于完成游戏。使用静态方法方便在任何地方调用。

在操作中添加音效。

void enterNumber(int number) {
  if (selectedRow < 0 || selectedCol < 0) return;
  if (isFixed[selectedRow][selectedCol]) return;
  
  GameSoundManager.playTap();
  
  board[selectedRow][selectedCol] = number;
  
  if (hasConflict(selectedRow, selectedCol)) {
    GameSoundManager.playError();
  }
  
  update();
  _checkCompletion();
}

在enterNumber方法中添加音效调用。填入数字时播放tap音效,如果产生冲突则播放error音效。这种即时的声音反馈让用户更清楚地知道操作的结果。

游戏的震动反馈也是重要的触觉体验。

void _vibrate(VibrationType type) {
  switch (type) {
    case VibrationType.light:
      HapticFeedback.lightImpact();
      break;
    case VibrationType.medium:
      HapticFeedback.mediumImpact();
      break;
    case VibrationType.heavy:
      HapticFeedback.heavyImpact();
      break;
    case VibrationType.error:
      HapticFeedback.vibrate();
      break;
  }
}

enum VibrationType { light, medium, heavy, error }

HapticFeedback提供了不同强度的震动反馈。light用于普通点击,medium用于重要操作,heavy用于完成游戏,error用于错误提示。这种触觉反馈让用户在不看屏幕的情况下也能感知操作结果。

游戏的手势快捷操作可以提升效率。

GestureDetector(
  onDoubleTap: () {
    if (!controller.isFixed[row][col] && controller.board[row][col] != 0) {
      controller.eraseCell();
    }
  },
  onLongPress: () {
    controller.toggleNotesMode();
    controller.selectCell(row, col);
  },
  onTap: () => controller.selectCell(row, col),
  child: _buildCellContent(row, col),
)

双击可以快速擦除单元格,长按可以切换到笔记模式并选中单元格。这些手势快捷操作让熟练的玩家可以更高效地操作,减少点击次数。

游戏的撤销确认可以防止误操作。

void undoMove() {
  if (moveHistory.isEmpty) {
    Get.snackbar('提示', '没有可撤销的操作');
    return;
  }
  
  GameMove lastMove = moveHistory.removeLast();
  
  if (lastMove.previousValue != null) {
    board[lastMove.row][lastMove.col] = lastMove.previousValue!;
  }
  if (lastMove.previousNotes != null) {
    notes[lastMove.row][lastMove.col] = lastMove.previousNotes!;
  }
  
  update();
}

当没有可撤销的操作时,显示提示信息而不是静默失败。Get.snackbar是GetX提供的便捷方法,可以快速显示底部提示条。这种反馈让用户知道为什么操作没有效果。

游戏的难度提示可以帮助新手。

Widget _buildDifficultyHint() {
  String hint;
  switch (controller.difficulty) {
    case 'Easy':
      hint = '简单难度:适合初学者,大部分数字已填入';
      break;
    case 'Medium':
      hint = '中等难度:需要一些推理技巧';
      break;
    case 'Hard':
      hint = '困难难度:需要高级解题技巧';
      break;
    case 'Expert':
      hint = '专家难度:极具挑战性,需要多种技巧组合';
      break;
    default:
      hint = '';
  }
  
  return Text(
    hint,
    style: TextStyle(fontSize: 12.sp, color: Colors.grey),
    textAlign: TextAlign.center,
  );
}

根据当前难度显示相应的提示信息,帮助玩家了解当前谜题的难度级别和所需技巧。这对于新手玩家特别有帮助。

总结一下游戏主界面的完整功能体系。核心是布局管理和状态响应,在此基础上我们添加了生命周期处理、进度保存恢复、音效震动反馈、手势快捷操作等功能。这些功能的组合让游戏主界面不仅功能完整,还具有良好的用户体验。通过合理的架构设计,代码保持清晰可维护,便于后续扩展新功能。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐