通过java将数据导出为PDF,包扣合并单元格操作

news2024/12/25 1:16:17

最近项目中需要将查询出来的表格数据以PDF形式导出,并且表格的形式包含横向行与纵向列的单元格合并操作,导出的最终效果如图所示:

首先引入操作依赖

<!--导出pdf所需包-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

最上面的基本信息是固定死的就是4*4的表格,这个创建起来 就比较简单,主要是下面这个表格,需要从数据库查出数据并循环进行展示,并且内容相同的列要进行合并。直接展示代码:

主类对外调用方法:

@Operation(summary = "导出PDF")
    @PostMapping("/download")
    @SneakyThrows(Exception.class)
    public void download(Long id,HttpServletResponse response, HttpServletRequest request) {
        // 防止日志记录获取session异常
        request.getSession();
        // 设置编码格式
        response.setContentType("application/pdf;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("调试PDF", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");
        contractDemandThreeRequestBaseService.download(id,response);
    }

具体实现类:

@Override
    public void download(Long id, HttpServletResponse response) {
        //要下载的数据查询数据部分我去掉了有需要自己根据业务取
        ContractDemandThreeRequestBaseDO baseDO = contractDemandThreeRequestBaseMapper.selectById(id);
        ContractDemandThreeRequestBaseRespVO base = ContractDemandThreeRequestBaseConvert.INSTANCE.convert(baseDO);
        base.setDeptName(ObjectUtil.isEmpty(deptService.getDept(base.getDeptId())) ? null : deptService.getDept(base.getDeptId()).getName());
        base.setProjectLeaderName(ObjectUtil.isEmpty(userService.getUser(base.getProjectLeaderId())) ? null : userService.getUser(base.getProjectLeaderId()).getNickname());
        //子表数据
        List<ContractDemandThreeQuestionDO> details = contractDemandThreeQuestionMapper.
                selectList(Wrappers.lambdaQuery(ContractDemandThreeQuestionDO.class).eq(ContractDemandThreeQuestionDO::getDemandId, id));
        //下面进行表格的创建、字体设置、合并单元格
        // 定义全局的字体静态变量
        Font titlefont;
        Font headfont;
        Font keyfont = null;
        Font textfont = null;
        Font content = null;
        BaseFont bfChinese = null;
        // 最大宽度
        try {
            // 不同字体(这里定义为同一种字体:包含不同字号、不同style)这里我用的最后一个                content字体
            bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            titlefont = new Font(bfChinese, 16, Font.BOLD);
            headfont = new Font(bfChinese, 14, Font.BOLD);
            keyfont = new Font(bfChinese, 10, Font.BOLD);
            textfont = new Font(bfChinese, 15, Font.NORMAL);
            content = new Font(bfChinese, 10, Font.NORMAL);
        } catch (Exception e) {
            e.printStackTrace();
        }
        BaseFont bf;
        Font font = null;
        try {
            //创建字体
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.NOT_EMBEDDED);
            //使用字体并给出颜色
            font = new Font(bf, 20, Font.BOLD, BaseColor.BLACK);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Document document = new Document();
        try {
            PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
            //打开生成的pdf文件
            document.open();
            //设置内容
            Paragraph paragraph = new Paragraph("采购需求三问", font);
            paragraph.setAlignment(1);
            //引用字体
            document.add(paragraph);
            // 设置表格的列宽和列数
            float[] widths = {25f, 25f, 25f, 25f};
            PdfPTable table = new PdfPTable(widths);
            table.setSpacingBefore(20f);
            // 设置表格宽度为100%
            table.setWidthPercentage(100.0F);
            table.setHeaderRows(1);
            table.getDefaultCell().setHorizontalAlignment(1);
            PdfPCell cell = null;
            //第一行:表格的名字
            cell = new PdfPCell(new Paragraph("采购项目名称", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            //表格里面的值(下面都是同样的操作)
            cell = new PdfPCell(new Paragraph(base.getProjectName(), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);

            //第二行
            cell = new PdfPCell(new Paragraph("项目编号", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(base.getProjectCode(), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph("资金类型", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(getDitcValue("FundType", base.getFundType()), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);

            //第三行
            cell = new PdfPCell(new Paragraph("支出类别", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(getDitcValue("ExpenditureCategory", base.getExpenditureCategory()), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph("预算含税总金额(万元)", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(String.valueOf(base.getBudgetIncludingTaxTotalMoney()), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            //第三行
            cell = new PdfPCell(new Paragraph("采购内容", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(base.getProcureContent(), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            //第四行
            cell = new PdfPCell(new Paragraph("需求部门", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(base.getDeptName(), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph("项目负责人", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(30);
            table.addCell(cell);
            cell = new PdfPCell(new Paragraph(base.getProjectLeaderName(), content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);

            // 设置表格的列宽和列数
            float[] widths2 = {25f, 25f, 25f};
            PdfPTable table2 = new PdfPTable(widths2);
            table2.setSpacingBefore(20f);
            // 设置表格宽度为100%
            table2.setWidthPercentage(100.0F);
            table2.setHeaderRows(1);
            table2.getDefaultCell().setHorizontalAlignment(1);
            //需求三问详情信息标题栏
            cell = new PdfPCell(new Paragraph("需求三问", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setFixedHeight(20);
            table2.addCell(cell);
            cell = new PdfPCell(new Paragraph("选择项", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table2.addCell(cell);
            cell = new PdfPCell(new Paragraph("内容项", content));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table2.addCell(cell);

            //下面的代码就是样图中底下的表格,需要动态构建数据
            //人员列表数据-第五行
            List<List<String>> doList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(details)) {
                //组装数据
                for (ContractDemandThreeQuestionDO detail : details) {
                    String natureType = "项目" + getDitcValue("QuestionType", detail.getDemandNatureType());
                    String deviceGoodsType = getDitcValue(baseDO.getModelType(), detail.getItemValue());
                    doList.add(Arrays.asList(natureType, deviceGoodsType, detail.getItemContent()));
                }
                makeData(doList, content, table2);
            //为两个表格添加标题
            document.add(new Paragraph("\n"));
            document.add(new Paragraph("▋ 基本信息", content));
            document.add(new Paragraph("\n"));
            document.add(table);
            document.add(new Paragraph("\n"));
            document.add(new Paragraph("▋ 采购需求三问内容", content));
            document.add(new Paragraph("\n"));
            //将table2中数据相同的列合并单元格(横向合并)
            document.add(table2);

            // 加水印(水印组成:下载人姓名-手机号-部门)
            PdfContentByte waterMar = pdfWriter.getDirectContentUnder();
            AtomicReference<String> text = new AtomicReference<>("");
            Long loginUserId = getLoginUserId();
            AdminUserRedisVO adminUserRedisVO = adminUserRedisDAO.get(loginUserId);
            Optional.ofNullable(adminUserRedisVO).ifPresent(vo -> {
                DeptDO dept = deptService.getDept(adminUserRedisVO.getDeptId());
                String deptName = Objects.nonNull(dept) ? dept.getName() : "";
                text.set(vo.getNickname() + "-" + vo.getMobile() + "-" + deptName);
            });
            addTextFullWaterMark(waterMar, text.get(), bfChinese);
            //关闭文档
            document.close();
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
            log.error("导出pdf失败:{}", e);
        }
    }

补充说明:List<List<String>> doList = new ArrayList<>();

这个是我构建底下表格数据的格式,举个例子,类似于:

List<List<String>> headList = new ArrayList<>();
headList.add(Arrays.asList(new String[]{"1", "2", "3"}));
headList.add(Arrays.asList(new String[]{"2", "6", "10"}));
headList.add(Arrays.asList(new String[]{"1", "12", "13"}));
headList.add(Arrays.asList(new String[]{"2", "9", "11"}));
headList.add(Arrays.asList(new String[]{"1", "8", "12"}));

根据自己的实际业务构建,我底下的表格是一个n*3的表格,只有三列,所以数据结构就相当于上面的两层List结构,最外层的集合代表有多少行,嵌套的结合相当于多少列(这里就是三列),我将对象集合查询出来后使用循环,将我所用到的数据组装成这个示例的样子。

重点来了,以下是合并单元格的方法:

makeData(doList, content, table2);
/**
     * 合并单元格方法
     *
     * @param list        表头数据  list中相连下标位置内容如果相同自动合并 上下位置内容相同自动合并
     * @param fontChinese 支持转换中文的Font对象
     * @return
     */
    private void makeData(List<List<String>> list, Font fontChinese, PdfPTable table2) {
        List<List<PdfPCell>> aa = new ArrayList<>();
        int length = list.get(0).size();
        //循环在外层,这里代表的是有几列(其实是固定的,因为上面构建的时候,列数就是三列)
        //下面的循环不用管,是将你组装的数据设置到单元格里
        for (int i = 0; i < list.size(); i++) {
            List<String> strings = list.get(i);
            int colNum = 1;
            List<PdfPCell> bb = new ArrayList<>();
            for (int j = 0; j < strings.size(); j++) {
                if (j + 1 < strings.size()) {
                    if (strings.get(j).equals(strings.get(j + 1))) {
                        colNum++;
                    } else {
                        PdfPCell cell = new PdfPCell();
                        //合并列
                        cell.setColspan(colNum);
                        Paragraph elements = new Paragraph(strings.get(j), fontChinese);
                        elements.setAlignment(1);
                        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                        cell.setFixedHeight(30);
                        cell.addElement(elements);
                        bb.add(cell);

                        for (int a = 1; a < colNum; a++) {
                            bb.add(null);
                        }
                        colNum = 1;
                    }
                } else {
                    PdfPCell cell = new PdfPCell();
                    cell.setColspan(colNum);
                    Paragraph elements = new Paragraph(strings.get(j), fontChinese);
                    cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                    cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                    elements.setAlignment(1);
                    cell.setFixedHeight(30);
                    cell.addElement(elements);
                    bb.add(cell);
                    for (int a = 1; a < colNum; a++) {
                        bb.add(null);
                    }
                    colNum = 1;
                }
            }
            aa.add(bb);
        }
        //合并算法

        for (int i = 0; i < length; i++) {

            int rowSpan = 1;

            for (int j = 0; j < aa.size(); j++) {

                if (aa.get(j).get(i) == null) {
                    continue;
                }

                if (j + 1 < aa.size()) {

                    if (aa.get(j + 1).get(i) != null
                            && aa.get(j).get(i).getCompositeElements().get(0).toString()
                            .equals(aa.get(j + 1).get(i).getCompositeElements().get(0).toString())
                    ) {
                        rowSpan++;
                    } else {
                        aa.get(j - rowSpan + 1).get(i).setRowspan(rowSpan);

                        for (int a = 1; a < rowSpan; a++) {
                            aa.get(j - rowSpan + 1 + a).set(i, null);
                        }
                        rowSpan = 1;
                    }
                } else {
                    aa.get(j - rowSpan + 1).get(i).setRowspan(rowSpan);

                    for (int a = 1; a < rowSpan; a++) {
                        aa.get(j - rowSpan + 1 + a).set(i, null);
                    }
                    rowSpan = 1;
                }
            }
            break;
        }

        for (List<PdfPCell> a : aa) {
            for (PdfPCell pCell : a) {
                if (pCell != null) {
                    table2.addCell(pCell);
                }
            }
        }

    }

这里将合并算法进行一个说明:外层循环其实还就是表示列,我这里是三列,他会依次循环三列,并将横向与纵向的表格中有相同数据的都进行合并,也就是值相同的行进行合并,值相同的列也进行合并。但我的业务要求就是只合并第一列,所以我这里也没有改算法,偷了个懒,直接在第一次循环后加了break,直接终止后面的循环。这个自己看自己的业务要求。代码里的getDictValue方法是我自己获取字典值的方法,可自行定义

加水印:

public static void addTextFullWaterMark(PdfContentByte waterMar, String text, BaseFont bf) {
        waterMar.beginText();

        PdfGState gs = new PdfGState();
        // 设置填充字体不透明度为0.2f
        gs.setFillOpacity(0.2f);
        waterMar.setFontAndSize(bf, 20);
        // 设置透明度
        waterMar.setGState(gs);
        // 设置水印对齐方式 水印内容 X坐标 Y坐标 旋转角度
        for (int x = 0; x <= 900; x += 200) {
            for (int y = -50; y <= 800; y += 200) {
                waterMar.showTextAligned(Element.ALIGN_RIGHT, text, x, y, 35);
            }
        }
        // 设置水印颜色
        waterMar.setColorFill(BaseColor.GRAY);
        //结束设置
        waterMar.endText();
        waterMar.stroke();
    }

这个操作PDF的类很强大,基本上你想怎么导出,都可以进行调整

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

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

相关文章

Linux 生态与工具

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 目录 Linux生态简介:Linux工具lrzsz&#xff…

适用于 Windows 8/10/11 的 10 大 PC 迁移工具:电脑克隆迁移软件

当您发现自己拥有一台新的 PC 或笔记本电脑时&#xff0c;PC 迁移变得至关重要。将数据从旧计算机传输到新计算机的过程似乎令人生畏&#xff0c;尤其是如果您是第一次这样做。迁移过程中数据丢失的潜在风险加剧了焦虑。为确保文件和系统设置的无缝无忧传输&#xff0c;使用专为…

探索设计模式的魅力:机器学习赋能,引领“去中心化”模式新纪元

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 探索设计模式的魅力&#xff1a;机器学习赋能&#xff0c;引领“去中心化”模式新纪元 ✨欢迎加入…

3月份太阳镜行业线上市场销售数据分析

在消费者行为方面&#xff0c;太阳镜不仅仅是视力保护工具&#xff0c;更逐渐成为一种时尚单品。随着人们对健康和美容重视程度的提高&#xff0c;太阳镜作为体现个人风格的单品&#xff0c;其市场需求得到了进一步的推动。此外&#xff0c;全球旅行和旅游业的恢复&#xff0c;…

被暗示离职?教你优雅反击

在职场中&#xff0c;面对公司暗示离职的情况&#xff0c;应届毕业生可能会感到困惑与无助。但是&#xff0c;保持冷静和据理力争是保护自己权益的重要途径。以下是如何应对此类情况的一些建议。 当你感觉到被暗示离职时&#xff0c;首要的策略就是与上司进行有效沟通。安排一个…

halo博客--解决恶意刷评论的问题

原文网址&#xff1a;halo博客--解决恶意刷评论的问题_IT利刃出鞘的博客-CSDN博客 简介 本文介绍halo博客如何通过设置评论次数来解决恶意刷评论的问题。 评论功能要设置频率的限制&#xff0c;否则可能被人一直刷评论&#xff0c;然后数据库存的垃圾评论越来越多&#xff0…

数据结构——队列(链表实现)

一、队列的特点 先进先出 二、队列的代码 typedef int QDataType;// 链式结构&#xff1a;表示队列 typedef struct QListNode {struct QListNode* next;QDataType data; }QNode;// 队列的结构 typedef struct Queue {QNode* front; //指向队列的第一个结点QNode* rear;//指…

刷代码随想录有感(66):回溯算法——组合问题的优化(剪枝)

代码&#xff1a;将for循环中i的搜索范围进行缩小&#xff0c;免去多余的不可能符合条件的操作。 for(int i start; i < n-(k-tmp.size())1;i) 实质是剪枝&#xff0c;拿n4,k4作比较&#xff1a; 显然结果只可能是[1,2,3,4]&#xff0c;选取顺序只可能是1-2-3-4&#xff…

Day27 代码随想录打卡|栈与队列篇---删除字符串中的所有相邻重复项

题目&#xff08;leecode T1047&#xff09;&#xff1a; 给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&#xff0c;直到无法继续删除。 在完成所有重复项删除操作后返回最终…

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

Muse论文精读

Muse Abstract 我们介绍了Muse&#xff0c;一个文本到图像的Transformer模型&#xff0c;它实现了最先进的图像生成性能&#xff0c;同时比扩散或自回归模型更有效。Muse是在离散标记空间中的掩码建模任务上进行训练的:给定从预训练的大型语言模型(LLM)中提取的文本嵌入&…

C语言如何删除表中指定位置的结点?

一、问题 如何删除链表中指定位置的结点&#xff1f; 二、解答 删除链表中指定的结点&#xff0c;就像是排好队的⼩朋友⼿牵着⼿&#xff0c;将其中⼀个⼩朋友从队伍中分出来&#xff0c;只需将这个⼩朋友的双⼿从两边松开。 删除结点有两种情况&#xff1a; &#xff08;1&am…

三菱FX3U-4AD模拟量电压输入采集实例

硬件&#xff1a;&#xff30;&#xff2c;&#xff23;模块 &#xff26;&#xff38;&#xff13;&#xff27;&#xff21;-&#xff12;&#xff14;&#xff2d;&#xff34; &#xff1b;&#xff21;&#xff0f;&#xff24;模块&#xff26;&#xff38;&#xff13…

连接虚拟机的 redis

用Windows 的 Redis Insight 连接虚拟机的 安装redis发现连不上 我的redis是新安装&#xff0c;没有用户名密码&#xff0c;发现是ip问题 127 开头的被我注释了&#xff0c;换成了ifconfig查到的ip

Nginx 生产环境部署的最佳实践

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 最近一段时间&#xff0c;我一直在和大家一起探讨Nginx的相关话题。期间&#xff0c;我收到了很多小伙伴的私信&#xff0c;他们好奇地问我&#xff1a;在生产环境中&#xff0c;Nginx应该如何配置&#xff1f; 他们在…

idea启动Jsp非maven项目时的一些步骤

文章目录 事前准备eclipse项目举例idea打开eclipse项目安装tomcat配置启动项启动测试 一些小问题到不到servlet 事前准备 非社区版idea【否则启动项无法配置】tomcatmysql eclipse项目举例 idea打开eclipse项目 剩下的全部下一步即可 安装tomcat 自己的文章 Javaweb - t…

《云原生安全攻防》-- 构建云原生攻防场景

在本节课程中&#xff0c;我们将学习云原生攻防场景的构建。为了研究云原生安全攻击案例&#xff0c;我们需要搭建一个云原生攻击测试环境&#xff0c;以便进行攻防研究和攻击手法的复现。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; 构建云原生攻防场景&#xf…

设计模式-动态代理

目录 定义 代理模式的优缺点 优点 缺点 应用场景 静态代理 动态代理 相关资料 定义 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它的概念很简单&#xff0c;它通过创建一个代理对象来控制对原始对象的访问。代理模式主要涉及两个…

Spring WebFlux 初探-响应式编程-021

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Nex…

Unity射击游戏开发教程:(17)添加推进器推进和推进器推进动画

添加推进器打开功能 我们可以添加一个推进器栏,用于跟踪玩家使用推进器增强(按住左 Shift 键)的时间。当未使用推力时,将会有一段延迟,直到推力条开始再生。当棒再生时,可以使用推进器,但再生过程将重新开始。 我们将使用 Unity 的 UI Slider 组件,因此我们将其添加到已…