鸿蒙HarmonyOS 5军旗小游戏实现指南

news2025/6/10 16:19:14

1. 项目概述

本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。

2. 项目结构

/src/main/java/com/example/militarychess/
    ├── MainAbilitySlice.java    // 主界面
    ├── GameView.java          // 游戏核心逻辑
    ├── ChessPiece.java        // 棋子类
    ├── GameLogic.java         // 游戏规则逻辑
    └── resources/
        ├── base/
        │   ├── layout/
        │   │   └── ability_main.xml  // 布局文件
        │   └── graphic/
        │       ├── piece_red.xml      // 红方棋子样式
        │       └── piece_blue.xml    // 蓝方棋子样式

3. 实现步骤

3.1 棋子类实现(ChessPiece.java)

package com.example.militarychess;

public class ChessPiece {
    public static final int RED = 0;
    public static final int BLUE = 1;
    
    // 棋子类型
    public static final int FLAG = 0;       // 军旗
    public static final int MINE = 1;       // 地雷
    public static final int BOMB = 2;       // 炸弹
    public static final int MARSHAL = 3;    // 司令
    public static final int GENERAL = 4;    // 军长
    public static final int DIVISION = 5;   // 师长
    public static final int BRIGADE = 6;    // 旅长
    public static final int REGIMENT = 7;   // 团长
    public static final int BATTALION = 8;  // 营长
    public static final int COMPANY = 9;    // 连长
    public static final int PLATOON = 10;   // 排长
    public static final int ENGINEER = 11;  // 工兵
    
    private int type;    // 棋子类型
    private int color;   // 棋子颜色
    private int row;     // 行
    private int col;     // 列
    private boolean visible = false; // 是否可见
    
    public ChessPiece(int type, int color, int row, int col) {
        this.type = type;
        this.color = color;
        this.row = row;
        this.col = col;
    }
    
    // getter和setter方法
    public int getType() { return type; }
    public int getColor() { return color; }
    public int getRow() { return row; }
    public void setRow(int row) { this.row = row; }
    public int getCol() { return col; }
    public void setCol(int col) { this.col = col; }
    public boolean isVisible() { return visible; }
    public void setVisible(boolean visible) { this.visible = visible; }
    
    // 获取棋子名称
    public String getName() {
        String[] names = {"军旗", "地雷", "炸弹", "司令", "军长", "师长", 
                         "旅长", "团长", "营长", "连长", "排长", "工兵"};
        return names[type];
    }
    
    // 获取棋子简称
    public String getShortName() {
        String[] names = {"旗", "雷", "弹", "司", "军", "师", 
                         "旅", "团", "营", "连", "排", "工"};
        return names[type];
    }
}

3.2 游戏视图实现(GameView.java)

package com.example.militarychess;

import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.agp.components.AttrSet;
import ohos.multimodalinput.event.TouchEvent;

import java.util.List;

public class GameView extends Component implements Component.DrawTask {
    private static final int ROWS = 12; // 棋盘行数
    private static final int COLS = 5;  // 棋盘列数
    
    private Paint linePaint;            // 棋盘线画笔
    private Paint redPiecePaint;       // 红方棋子画笔
    private Paint bluePiecePaint;      // 蓝方棋子画笔
    private Paint textPaint;           // 文字画笔
    private Paint selectedPaint;       // 选中状态画笔
    
    private List<ChessPiece> pieces;   // 棋子列表
    private ChessPiece selectedPiece;  // 当前选中的棋子
    private int currentPlayer = ChessPiece.RED; // 当前玩家
    
    public GameView(Context context) {
        super(context);
        init();
    }
    
    public GameView(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init();
    }
    
    private void init() {
        linePaint = new Paint();
        linePaint.setColor(new Color(0xFF000000));
        linePaint.setStrokeWidth(2);
        
        redPiecePaint = new Paint();
        redPiecePaint.setColor(new Color(0xFFFF0000));
        redPiecePaint.setStyle(Paint.Style.FILL_STYLE);
        
        bluePiecePaint = new Paint();
        bluePiecePaint.setColor(new Color(0xFF0000FF));
        bluePiecePaint.setStyle(Paint.Style.FILL_STYLE);
        
        textPaint = new Paint();
        textPaint.setColor(new Color(0xFFFFFFFF));
        textPaint.setTextSize(40);
        textPaint.setTextAlign(Paint.Align.CENTER);
        
        selectedPaint = new Paint();
        selectedPaint.setColor(new Color(0x6600FF00));
        selectedPaint.setStyle(Paint.Style.FILL_STYLE);
        
        initPieces();
        addDrawTask(this);
        setTouchEventListener(this::onTouchEvent);
    }
    
    private void initPieces() {
        // 初始化棋子布局
        // 这里应该实现具体的棋子初始化逻辑
        // 包括红蓝双方的棋子摆放
    }
    
    @Override
    public void onDraw(Component component, Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        float cellWidth = width / (float) COLS;
        float cellHeight = height / (float) ROWS;
        
        // 绘制棋盘
        drawBoard(canvas, width, height, cellWidth, cellHeight);
        
        // 绘制棋子
        drawPieces(canvas, cellWidth, cellHeight);
    }
    
    private void drawBoard(Canvas canvas, int width, int height, float cellWidth, float cellHeight) {
        // 绘制横线
        for (int i = 0; i <= ROWS; i++) {
            float y = i * cellHeight;
            canvas.drawLine(0, y, width, y, linePaint);
        }
        
        // 绘制竖线
        for (int j = 0; j <= COLS; j++) {
            float x = j * cellWidth;
            canvas.drawLine(x, 0, x, height, linePaint);
        }
        
        // 绘制行营和大本营
        drawSpecialAreas(canvas, cellWidth, cellHeight);
    }
    
    private void drawSpecialAreas(Canvas canvas, float cellWidth, float cellHeight) {
        // 实现行营和大本营的特殊标记
    }
    
    private void drawPieces(Canvas canvas, float cellWidth, float cellHeight) {
        for (ChessPiece piece : pieces) {
            float centerX = (piece.getCol() + 0.5f) * cellWidth;
            float centerY = (piece.getRow() + 0.5f) * cellHeight;
            float radius = Math.min(cellWidth, cellHeight) * 0.4f;
            
            // 绘制棋子背景
            Paint bgPaint = piece.getColor() == ChessPiece.RED ? redPiecePaint : bluePiecePaint;
            canvas.drawCircle(centerX, centerY, radius, bgPaint);
            
            // 绘制棋子文字
            if (piece.isVisible() || piece.getColor() == currentPlayer) {
                canvas.drawText(piece.getShortName(), centerX, centerY + 15, textPaint);
            }
            
            // 绘制选中状态
            if (piece == selectedPiece) {
                canvas.drawCircle(centerX, centerY, radius + 5, selectedPaint);
            }
        }
    }
    
    private boolean onTouchEvent(Component component, TouchEvent event) {
        if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            int width = getWidth();
            int height = getHeight();
            float cellWidth = width / (float) COLS;
            float cellHeight = height / (float) ROWS;
            
            int col = (int) (event.getPointerPosition(0).getX() / cellWidth);
            int row = (int) (event.getPointerPosition(0).getY() / cellHeight);
            
            if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
                handleClick(row, col);
            }
        }
        return true;
    }
    
    private void handleClick(int row, int col) {
        ChessPiece clickedPiece = getPieceAt(row, col);
        
        if (selectedPiece == null) {
            // 没有选中棋子时,尝试选中一个棋子
            if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
                selectedPiece = clickedPiece;
                invalidate();
            }
        } else {
            // 已经选中了一个棋子
            if (clickedPiece != null && clickedPiece.getColor() == currentPlayer) {
                // 点击的是己方棋子,切换选中
                selectedPiece = clickedPiece;
                invalidate();
            } else {
                // 尝试移动棋子
                if (isValidMove(selectedPiece, row, col)) {
                    movePiece(selectedPiece, row, col);
                }
            }
        }
    }
    
    private ChessPiece getPieceAt(int row, int col) {
        for (ChessPiece piece : pieces) {
            if (piece.getRow() == row && piece.getCol() == col) {
                return piece;
            }
        }
        return null;
    }
    
    private boolean isValidMove(ChessPiece piece, int toRow, int toCol) {
        // 实现军棋移动规则判断
        // 包括铁路线移动、普通移动、吃子规则等
        return true;
    }
    
    private void movePiece(ChessPiece piece, int toRow, int toCol) {
        // 实现棋子移动逻辑
        // 包括吃子判断、胜负判断等
        
        // 移动完成后切换玩家
        currentPlayer = (currentPlayer == ChessPiece.RED) ? ChessPiece.BLUE : ChessPiece.RED;
        selectedPiece = null;
        invalidate();
    }
    
    public void resetGame() {
        initPieces();
        currentPlayer = ChessPiece.RED;
        selectedPiece = null;
        invalidate();
    }
}

3.3 主界面实现(MainAbilitySlice.java)

package com.example.militarychess;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.window.dialog.ToastDialog;

public class MainAbilitySlice extends AbilitySlice {
    private GameView gameView;
    private Text statusText;
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        
        DirectionalLayout layout = new DirectionalLayout(this);
        layout.setOrientation(DirectionalLayout.VERTICAL);
        
        // 状态文本
        statusText = new Text(this);
        statusText.setText("当前回合: 红方");
        statusText.setTextSize(50);
        statusText.setPadding(10, 10, 10, 10);
        
        // 游戏视图
        gameView = new GameView(this);
        
        // 按钮布局
        DirectionalLayout buttonLayout = new DirectionalLayout(this);
        buttonLayout.setOrientation(DirectionalLayout.HORIZONTAL);
        buttonLayout.setPadding(10, 10, 10, 10);
        
        // 重新开始按钮
        Button resetButton = new Button(this);
        resetButton.setText("重新开始");
        resetButton.setClickedListener(component -> gameView.resetGame());
        
        // 规则说明按钮
        Button ruleButton = new Button(this);
        ruleButton.setText("游戏规则");
        ruleButton.setClickedListener(component -> showRules());
        
        buttonLayout.addComponent(resetButton);
        buttonLayout.addComponent(ruleButton);
        
        layout.addComponent(statusText);
        layout.addComponent(gameView);
        layout.addComponent(buttonLayout);
        
        super.setUIContent(layout);
    }
    
    private void showRules() {
        new ToastDialog(this)
            .setText("军棋规则:\n1. 工兵可以挖地雷\n2. 炸弹可以与任何棋子同归于尽\n3. 大本营内的棋子不能移动")
            .show();
    }
}

3.4 布局文件(ability_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent"
    ohos:orientation="vertical"
    ohos:background_element="#F5F5F5">
    
    <Text
        ohos:id="$+id:status_text"
        ohos:width="match_parent"
        ohos:height="50vp"
        ohos:text="当前回合: 红方"
        ohos:text_size="20fp"
        ohos:text_color="#FF0000"
        ohos:padding="10vp"
        ohos:margin="5vp"
        ohos:background_element="#FFFFFF"/>
        
    <com.example.militarychess.GameView
        ohos:id="$+id:game_view"
        ohos:width="match_parent"
        ohos:height="0vp"
        ohos:weight="1"
        ohos:margin="5vp"
        ohos:background_element="#FFFFFF"/>
        
    <DirectionalLayout
        ohos:id="$+id:button_layout"
        ohos:width="match_parent"
        ohos:height="wrap_content"
        ohos:orientation="horizontal"
        ohos:padding="10vp"
        ohos:margin="5vp"
        ohos:background_element="#FFFFFF">
        
        <Button
            ohos:id="$+id:reset_button"
            ohos:width="0vp"
            ohos:height="50vp"
            ohos:weight="1"
            ohos:text="重新开始"
            ohos:text_size="16fp"
            ohos:margin="5vp"/>
            
        <Button
            ohos:id="$+id:rule_button"
            ohos:width="0vp"
            ohos:height="50vp"
            ohos:weight="1"
            ohos:text="游戏规则"
            ohos:text_size="16fp"
            ohos:margin="5vp"/>
    </DirectionalLayout>
</DirectionalLayout>

4. 游戏规则实现要点

4.1 棋子移动规则

private boolean isValidMove(ChessPiece piece, int toRow, int toCol) {
    // 1. 检查目标位置是否合法
    if (toRow < 0 || toRow >= ROWS || toCol < 0 || toCol >= COLS) {
        return false;
    }
    
    // 2. 检查是否是大本营(不能移动)
    if (isHeadquarters(piece.getRow(), piece.getCol())) {
        return false;
    }
    
    // 3. 铁路线移动规则
    if (isRailway(piece.getRow(), piece.getCol())) {
        // 铁路线上可以任意距离直线移动
        return canMoveOnRailway(piece, toRow, toCol);
    }
    
    // 4. 普通移动规则(一步一格)
    int rowDiff = Math.abs(toRow - piece.getRow());
    int colDiff = Math.abs(toCol - piece.getCol());
    
    return (rowDiff == 1 && colDiff == 0) || (rowDiff == 0 && colDiff == 1);
}

4.2 吃子规则

private boolean canEat(ChessPiece attacker, ChessPiece defender) {
    // 1. 炸弹可以和任何棋子同归于尽
    if (attacker.getType() == ChessPiece.BOMB || defender.getType() == ChessPiece.BOMB) {
        return true;
    }
    
    // 2. 工兵可以挖地雷
    if (attacker.getType() == ChessPiece.ENGINEER && defender.getType() == ChessPiece.MINE) {
        return true;
    }
    
    // 3. 地雷只能被工兵挖
    if (defender.getType() == ChessPiece.MINE) {
        return false;
    }
    
    // 4. 军旗不能被移动
    if (defender.getType() == ChessPiece.FLAG) {
        return false;
    }
    
    // 5. 普通棋子比较大小
    return attacker.getType() <= defender.getType();
}

5. 特殊功能实现

5.1 行营保护

private boolean isCamp(int row, int col) {
    // 定义行营位置
    int[][] camps = {{1, 1}, {1, 3}, {3, 1}, {3, 3}, 
                    {8, 1}, {8, 3}, {10, 1}, {10, 3}};
    
    for (int[] camp : camps) {
        if (camp[0] == row && camp[1] == col) {
            return true;
        }
    }
    return false;
}

5.2 铁路线移动

private boolean canMoveOnRailway(ChessPiece piece, int toRow, int toCol) {
    // 检查是否在同一条铁路线上
    if (piece.getRow() != toRow && piece.getCol() != toCol) {
        return false;
    }
    
    // 检查路径上是否有其他棋子阻挡
    if (piece.getRow() == toRow) {
        int minCol = Math.min(piece.getCol(), toCol);
        int maxCol = Math.max(piece.getCol(), toCol);
        
        for (int col = minCol + 1; col < maxCol; col++) {
            if (getPieceAt(piece.getRow(), col) != null) {
                return false;
            }
        }
    } else {
        int minRow = Math.min(piece.getRow(), toRow);
        int maxRow = Math.max(piece.getRow(), toRow);
        
        for (int row = minRow + 1; row < maxRow; row++) {
            if (getPieceAt(row, piece.getCol()) != null) {
                return false;
            }
        }
    }
    
    return true;
}

6. 扩展功能建议

  1. ​网络对战​​:实现双人在线对战功能
  2. ​AI对手​​:添加不同难度的电脑AI
  3. ​游戏回放​​:记录和回放游戏过程
  4. ​音效系统​​:添加移动、吃子等音效
  5. ​动画效果​​:棋子移动和战斗动画
  6. ​战绩统计​​:记录玩家胜负数据
  7. ​多种布局​​:提供不同的初始棋子布局

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring AOP代理对象生成原理

代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】&#xff0c;这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…

密码学基础——SM4算法

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​专栏主页&#xff1a;密码学 &#x1f4cc; 【今日更新】&#x1f4cc; 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 ​编辑…

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

基于单片机的宠物屋智能系统设计与实现(论文+源码)

本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢&#xff0c;连接红外测温传感器&#xff0c;可实时精准捕捉宠物体温变化&#xff0c;以便及时发现健康异常&#xff1b;水位检测传感器时刻监测饮用水余量&#xff0c;防止宠物…

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …

C# winform教程(二)----checkbox

一、作用 提供一个用户选择或者不选的状态&#xff0c;这是一个可以多选的控件。 二、属性 其实功能大差不差&#xff0c;除了特殊的几个外&#xff0c;与button基本相同&#xff0c;所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践

01技术背景与业务挑战 某短视频点播企业深耕国内用户市场&#xff0c;但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大&#xff0c;传统架构已较难满足当前企业发展的需求&#xff0c;企业面临着三重挑战&#xff1a; ① 业务&#xff1a;国内用户访问海外服…

CSS3相关知识点

CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇

根据 QYResearch 发布的市场报告显示&#xff0c;全球市场规模预计在 2031 年达到 9848 万美元&#xff0c;2025 - 2031 年期间年复合增长率&#xff08;CAGR&#xff09;为 3.7%。在竞争格局上&#xff0c;市场集中度较高&#xff0c;2024 年全球前十强厂商占据约 74.0% 的市场…