版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第10章 多媒体编程,教学目的要求 1.掌握Java语言播放声音、处理图形和图像的方法; 2.掌握Java语言实现动画的方法,并能够对其实现进行优化。,10.1 检索与使用声音文件,简单声音文件播放 多媒体功能在Applet中是必不少的一部分,优美的音乐,漂亮的界面往往是一个成功网页所必需具备的条件。在JDK1.0上,Java只支持以单声道8KHz的采样频率存储的au格式的声音文件,但是,Java2的API以及声音包提供了强大的音频格式的支持,Java2平台增加了对AIFF、WAV以及MIDI等声音文件类型的支持。,由于Java采用了JVM虚拟机机制,一个Java程序就可以运行在任何一个安装了
2、Java 虚拟机的系统上,这样就使得Java程序与操作平台无关。因此,在Java环境下播放声音的方法与计算机硬件无关,从而简化了Java处理多媒体的编程难度。Java在Applet小应用程序中提供的最简单的声音播放技术是使用Applet类中的play()方法播放声音文件。 下面就介绍一下这种播放声音文件的方法。play()方法的格式如下: public void play(URL url); public void play(URL url, String name); play(URL url)方法能播放URL地址为url的声音文件,而play(URL url,String name)方法则
3、能播放URL地址为url、文件名为name的声音文件。如果系统没有找到该声音文件,计算机将不播放任何声音。,例10.1 简单声音文件播放应用举例。播放声音剪辑文件test.wav,该声音文件位于Applet小应用程序所在目录中。 /播放当前目录下的声音文件test.wav。Li10_01.java import java.awt.*; import java.applet.*; public class Li10_01 extends Applet public void start( ) play(getCodeBase( ),test.wav); public void paint(Gra
4、phics g) /用来在屏幕上显示正在播放的声音文件信息 g.drawString(正在播放音乐 + getCodeBase( ) + :test.wav, 5, 10); ,在运行本例程序前,读者要确认在Li10_01程序所在目录中存放有test.wav声音文件,而程序中的 getCodeBase()方法返回Applet小应用程序的URL。 play()方法只能播放声音文件一次,如果希望循环播放声音文件,或随时停止播放,则该方法不能满足需要。,AudioClip对象,在Java程序中,可以利用java.applet包中的AudioClip类进行播放声音文件,并且可以通过该类中的方法对声音文
5、件的播放进行高级控制。该类在java.applet.AudioClip类库中定义。下面介绍其具体实现: 1. 使用import语句引入该类库。格式如下: import java.applet.AudioClip; 或 import java.applet.* ;,Audio 声音的 Clip (被剪下的)东西,2. 创建AudioClip对象并用getaudioClip()方法将其初始化, 格式如下: AudioClip audio = getAudioClip(getCodeBase(), AudioFileName.au); 其中:audio是创建的AudioClip对象;getAudio
6、Clip()方法将声音文件从Internet 网络上装入Applet小应用程序;getCodeBase()返回Applet小应用程序的URL;AudioFileName.au是声音文件名,如果它位于Applet小应用程序目录的子目录中,还必须包含相应的路径。 3. 利用下面AudioClip类提供的3个有关声音播放的方法进行播放控制: play():播放声音文件一次。 loop():循环播放声音文件。 stop():停止正在播放或循环播放的声音文件。 AudioClip接口是一个播放声音文件的简单抽象,每一个AudioClip都可以独立播放,并且多个AudioClip也可以同时播放,最终的声音
7、是一个混合的合成声音。,把声音加载到AudioClip对象中,启动、停止和循环播放声音文件,可以用applet的getAudioClip()方法把它装载进入AudioClip对象,利用getAudioClip()方法加载声音文件有两种格式: AudioClip getAudioClip ( URL url ); AudioClip getAudioClip ( URL url , String name ); getAudioClip()方法的参数URL指明声音文件的位置,参数name是指明声音文件名。,假设“gun.wav”是指当前目录下的声音文件,下面的代码行说明加载声音文件进入剪贴对象:
8、 AudioClip co = getAudioClip( getCodeBase( ), gun.wav); getAudioClip()方法仅仅能在applet内调用,在Java应用程序里是不能使用该方法的。随着JAVA2的引入,应用程序可以用Applet类的newAudioClip()方法装入声音文件。newAudioClip()方法格式为: public static final AudioClip newAudioClip(URLurl),前面使用getAudioClip()方法加载声音文件的代码可以改写为下面的代码以用于Java应用程序: AudioClip co = newAud
9、ioClip(“gun.wav”); 该方法也是 java.applet.Applet 类的一部分,可以用于Applet,也可以用于非Applet程序。Applet可以使用getCodeBase()和 getDocumentBase()获得的参数构造URL来使用音频文件,getCodeBase()方法获取Applet类文件所在位置的URL地址,返回的地址可以是因特网上的一个地址,也可以是本地磁盘上的一个位置。用于因特网地址的标准协议是http,本地地址的协议是File。getDocumentBase()方法获取HTML文档的URL地址。,现在,可在任何地方使用方法play()播放声音文件了。p
10、lay()一旦被调用立刻开始播放声音。但有一点要注意:如果声音文件不存在,AudioClip对象的值将是空的,但程序不会有出错信息,在编写程序过程中可以通过对该条件进行判断来检测声音文件是否存在。 例10.2 高级声音播放示例。下面是一个在Applet中利用AudioClip类控制声音文件播放的例子。 /高级声音播放示例。Li10_02.java import java.awt.*; import java.applet.*; import java.awt.event.*;,public class Li10_02 extends Applet implements ActionListen
11、er AudioClip sound; /创建AudioClip对象 Button playS = new Button(开始播放); /创建三个按钮 Button stopS = new Button(停止播放); Button loopS = new Button(循环播放); public void init() /applet程序运行后首先调用init()方法 playS.addActionListener(this); stopS.addActionListener(this); /动作监听器,监视按钮的动作以响应该事件 loopS.addActionListener(this);
12、/当响应事件时将调用actionPerformed()方法,add(playS); add(stopS); add(loopS); /使按钮显示在浏览器窗口中 try /用getAudioClip方法将AudioClip对象初始化,获 取声音文件名 sound = getAudioClip(getDocumentBase(),test.wav); catch(Exception e) public void actionPerformed(ActionEvent evn) Object obj = evn.getSource(); /getSource()将返回触发事件的按钮名称 if(obj
13、 = playS) /判断触发的事件按钮 sound.play( ); /播放一次声音文件 ,else if(obj = stopS) sound.stop( ); /停止播放声音文件 else sound.loop( ); /循环播放声音文件 编写如下的HTML文件,命名为Li10_02.htm保存,为运行该Applet做准备。后面的Applet程序也可以按如下格式改写即可。 播放声音的Java小应用程序 ,10.2 图形与动画,Java具有很强大的处理图形和动画的功能,它可以处理图形(Graphic)、图像(Image)以及制作动画(Animation)等。,图形与文本,Java处理图形最
14、基本也是最重要的类就是java.awt.Graphics类,用这个类可以完成绘制简单的图形,例如画线、写字、画几何形状等。 Java语言的类库中提供了丰富的绘图方法,其中大部分对图形、文本、图像的操作方法都定义在Graphics 类中。因为Graphics类是java.awt程序包的一部分,因此,每当进行图形、文本、图像的处理时,必须先把该类库导入Java程序,即在源文件的头部写上:“import java.awt.Graphics;”。,当想要在屏幕上绘制图形、文本、图像时,并不需要直接使用new来产生一个Graphics类的对象实例,只需要有一个画板,在这个画板上它就可以进行各种各样的画图
15、操作。画板是需要有一个实体的,而java.awt.Component类代表着所有的Java组件具有getGraphics()方法,返回一个图形对象,这样就可以在所有的Java组件上进行画图操作了。而在java.awt.Applet类的paint()方法中,可以得到一个Graphics对象的引用,这是系统直接将生成好的Graphics对象通过参数形式传递给paint()方法。因此,只要在这个对象上进行图形、文本及图像的绘制操作,就可以在屏幕上看到所显示的结果。,Graphics类的方法有很多, 对于部分方法读者可以在教材中查询到。 图形的绘制颜色一般是通过setColor(Color c)方法来
16、改变的,当需要以某一种新的颜色来绘制某一个图形时,就需要设定一种新的颜色对象然后通过setColor()方法来改变它。通常可以经过Color类的静态属性来创建一个指定颜色的颜色对象。如创建一个白色对象为: Color c = Color.WHITE; Color类的静态颜色属性是有很多的,常用的有WHITE(白色)、BLACK(黑色)、BLUE(蓝色)、CYAN(青色)、GRAY(灰色)、GREEN(绿色)、ORANGE(橙色)、PINK(粉红色)、RED(红色)、YELLOW(黄色)等。,当在绘制文字的时候,首先需要设定好字体。Graphics对象调用setFont(Font f)方法来设定
17、,字体设定好之后,就可以在指定的位置绘制字了。 例10.3 绘制简单的图形。 import java.awt.Graphics; import java.awt.Color; import java.awt.Font; public class Li10_03 extends java.applet.Applet public void paint(Graphics g) g.drawLine(6,150,50,50); /绘制直线 g.drawLine(6,150,50,150);,g.drawRect(60,30,100,30); /绘制矩形 g.draw3DRect(60,160,100
18、,30,true); /绘制3D矩形,true表示3D效果是凸的 g.drawRoundRect(60,200,100,40,10,10); /绘制圆角矩形 g.drawArc(200,50,10,8,0,45); g.drawOval(200,150,80,80); g.drawString(测试绘制文字,200,250); /绘制字符串 Color c = Color.RED; /创建一个红色颜色对象 g.setColor(c); /设置绘图颜色为红色 g.fillOval(200,100,100,40); /绘制填充椭圆 g.setColor(Color.PINK);,g.fill3DR
19、ect(60,80,100,30,true); /绘制三维矩形 g.setColor(Color.BLUE); g.fillArc(200,200,100,80,0,45); /绘制填充弧 Font f = new Font(黑体,Font.ITALIC,60); /创建字体对象 g.setFont(f); /设置绘图字体 g.drawString(欢迎访问,10,300); /绘制字符串 编译该程序并编写相应的HTML文档,运行后在浏览器窗口内将会绘制出各种图形和文字。由此可见,在Applet程序中画图是相当简单的,只要在paint()方法中写出画图语句即可。,图像,Image类是Java进
20、行动画的基础,Java支持两种常用的图像格式:JPEG和GIF,Image就是用已有的JPEG、GIF等图片生成的对象。在Applet中显示图像需要两个步骤:加载图像数据和显示图像。 1. 获取图像数据 Image picture=getImage(URL,String filename); 说明: (1)该方法在Applet中定义,返回值是一个Image类对象。,(2)Image是java.awt中的类,封装了图像的信息,并提供获取图像有关信息的方法,以便得到有关图像的属性。例如用getHeight(ImageObserver observer)获取图像的高度,用getWidth(Image
21、Observer observer)获取图像的宽度。两个获取图像高度和宽度的方法中的参数是ImageObserver(界面)型的对象,只定义一个方法:imageUpdate(),Component类实现了该界面。 (3)第一个参数为图像文件的URL地址,和获取音频文件URL地址一样,也可用Applet的方法:getCodeBase和getDocumentBase通过相对路径获取图像数据;第二个参数为图像文件名字符串,Java支持的图像文件有.gif和.jpg。 (4)编程时,加载图像数据的工作在init()方法中进行;图像的下载工作由awt自动生成一个线程在后台异步完成。,2. 显示图像 dr
22、awImage(Image img,int x,int y,int width,int height,Color bgcolor,ImageObserver observer); 说明: (1)该方法在Graphics类中定义,所以应该在Applet的paint()方法中借助Graphics类对象来调用。 (2)img是要显示的图像文件;(x,y)是指定显示位置的左上角坐标(Applet显示区的坐标系);width和height是指定显示图像的大小;bgcolor:指定图像的背景颜色;observer是跟踪图像下载情况的ImageObserver型参数。在调用异步方法时,都要给出一个这种参数,
23、以调用它的imageUpdate()方法对下载的图像不断更新。,例10.4 在浏览器窗口中显示图像示例。 import java.awt.*; import java.applet.*; public class Li10_04 extends Applet Image img; public void init() /构造函数,并初始化获取图像对象 img = getImage(getDocumentBase(),image/wu.jpg); public void paint(Graphics g) / Paint()方法用于绘制组件 g.drawImage(img,0,0,this);
24、Font f = new Font(楷体_GB2312,Font.ITALIC,48); g.setFont(f); g.setColor(Color.WHITE); g.drawString(这是一个测试,50,300);/用白色写 ,该程序显示HTML文档所在目录下image子目录中的wu.jpg图片,并且同时还在这个图片上显示“这是一个测试”文字。 上面介绍的只是简单图像加载的例子,但是当在一个Applet上同时加载几个图像时,会发现所有图像并非一下子显示出来,而是从顶部开始慢慢显示,同时整个画面伴随着强烈的闪烁,这种现象尤其是从网络上装载图像时会更加明显。,产生这种现象的原因就是图像数
25、据在尚未完全准备好的情况下就开始显示了,init()方法在调用getImage()方法把所有图像都装载进来的时候,仅仅是生成了一个Image对象返回给调用者,但这并不表示图像文件已经被装载到内存中,并且因为图像一般比较大所以从网络加载到内存中也需要一个过程,所以程序产生的加载图像的效果就不会那么完美,从而产生闪烁现象。为了解决这个问题,Java提供了java.awt.MediaTracker类。,MediaTracker是个非常有用的类,它能自动追踪一个或多个图片的装载进度,最简单的使用形式是,建立MediaTracker的一个实例,在载入图片对象之后,使用MediaTracker对象的add
26、Image()方法来添加需要跟踪的图片对象,然后在显示图片对象之前,使用waitForAll()方法跟踪图片对象的下载状态,当所有的图像的数据都装载完之后再进行显示,这时就不会出现因等待图像下载而出现的闪烁现象。MediaTracker类只有一个构造函数: MediaTracker (Component c);,它的含义是为给定组件创建一个跟踪媒体的MediaTracker对象。同时MediaTracker类还提供了一些常用的方法: addImage():把图片添加到所跟踪的图片组中。 checkAll():检查所有图片是否下载完毕。 waitForAll():开始载入所有图片。 waitFo
27、rID():开始载入所有带标识的图片。 例10.5 使用MediaTracker类显示图像的示例。在该例中将显示四个图像文件,该程序利用了MediaTracker类中的方法,在显示图像以前已先把图像读取到内存中。,/Li10_05.java import java.applet.Applet; import java.awt.*; public class Li10_05 extends Applet private Image testImage; private MediaTracker mt; /声明一个 MediaTracker 类实例对象 public void init() mt
28、= new MediaTracker(this); /对 mt 实例做初始化 testImage = new Image4; for(int i = 0;i 4;i+) testImagei= getImage(getDocumentBase(),ball+(i+1)+.jpg); /把图片加载到所跟踪的图片组中 mt.addImage(testImagei,i);,try mt.waitForAll(); /等待所有图片加载完 catch(InterruptedException ie) /waitForAll()方法会抛出InterruptedException /init() publi
29、c void paint(Graphics g) for(int i = 0; i 4; i+) if(mt.checkID(i,true) /检查标号为i的图片是否完成下载 g.drawImage(testImagei,80*i,0,this); /把内存中的图像拷贝到屏幕上 编译并运行本程序,在显示效果时会发现,四个图像几乎是同时被显示在屏幕上 。,动画,动画是由一系列的静态画面组成的,动画的本质就是运动的图形,一系列连续显示的静止图形会给我们一种连续动画的假象。Java中实现动画的基本原理是利用人眼的暂停效果在屏幕上产生一系列连续的图像来造成运动的效果。根据人的视觉原理,一般的动画播放速
30、率应达到每秒30帧左右才能保证动作的连续性,不会造成跳动的感觉。动画播放速率越高,所表现的动作就越细腻。,在计算机上实现动画,就必须不停地更新动画帧。为了能够不断地更新屏幕,需创建一个线程来循环实现动画,这个循环要跟踪当前帧并响应周期性的屏幕更新要求。 在Java中,不管是Applet程序还是Application程序,实现动画效果的关键就是使用线程,因为只有这样才可以不断地进行图像之间的切换而达到动画的目的。以下是在Java中实现动画线程的一般步骤: 1. 在Applet 类中实现Runnable 接口,其表现形式是: public class testAnimation extends A
31、pplet implements Runnable /. ,2. 程序最开始时执行init()方法,所以需在该方法内完成获取或加载图像的操作。 3. 改写start()方法。在其中产生一个动画线程并启动它,start()方法的写法如下: public void start () if (runner = null) runner = new Thread(this); runner.start(); ,4. 改写stop()方法,以使线程结束时释放内存。在其中编写结束线程的程序代码, stop()方法的写法: public void stop() runner = null ; 5. 编写ru
32、n()方法。启动了动画线程后,run()方法将被自动调用,控制动画的循环就放在这里,每次循环都会调用repaint()方法去绘制一帧图像。 6. 编写paint()方法,在该方法中绘制一帧图像。,例10.6 由7幅图片交替显示而产生动画效果的例子。两幅图像的文件名分别为1.gif 7.gif,存放于Applet小应用程序所在的目录内。 import java.applet.*; import java.awt.*; public class Li10_06 extends Applet implements Runnable Image images; int count = 0, x=-10
33、; Thread thread; public void init() /完成加载图像的操作 images = new Image7; MediaTracker tracker = new MediaTracker(this); for(int i = 0; i7; i+) imagesi = getImage(getCodeBase(), image/+ (i+1)+.gif); tracker.addImage(imagesi, 0); tracker.checkAll(true); ,/在Applet 类中实现Runnable 接口,/*在init()方法中也可使用注释的语句直接获取图像
34、文件,但本例中使用了MediaTracker类主要是为了使图像显示前先把全部图像下载到用户的内存中*/ public void start() /创建动画线程并启动该线程 thread = new Thread(this); thread.start(); public void stop() /改写stop()方法 thread = null; public void run() /启动线程后将自动调用run()方法,在run()方法内产生一个控制动画的循环 int delay = 200; while(true) repaint(); /repaint()将调用paint()方法画一帧图像
35、 try Thread.sleep(delay); catch (Exception e) ,public void paint(Graphics g) g.drawImage(imagescount+%7, x , 0, this); g.setColor(Color.red); for (int i=0;i600) x=-10; ,例10.7 利用绘图工具显示动画效果。通过绘图工具绘制一个红色圆,然后利用Java的动画功能使其从左向右移动。 import java.awt.*; import java.applet.*; public class Li10_07 extends Apple
36、t implements Runnable int xpos; /保存圆心的坐标,因为圆左右平移,所以Y坐标不变 Thread runner; boolean painted=false; public void init() xpos=0; ,public void start() if(runner =null) runner = new Thread(this); /创建一个新的动画线程 runner.start(); /启动线程 public void stop() if (runner != null) runner = null; /使用线程释放所占的资源 ,public void
37、 run() while (true) for (xpos=0; xpos400-90;xpos+=3) /产生一个循环,使圆心的X坐标不断更换 repaint(); try Thread. sleep (100); catch (InterruptedException e) if (painted) painted = false; public void paint (Graphics g) g.setColor(Color.white); /设置绘图颜色为白色 g.fillRect(0,0,getSize().width,getSize().height); /在绘图区绘图一个全屏的白
38、色矩形 g.setColor(Color.red); /设置绘图颜色为红色 g.fillOval(xpos, 20,90,90); /绘制一个填充的圆 painted = true; ,上面的两个程序都实现了帧动画连续播放的效果,但是两个程序在运行时都会发现帧动画播放过程中会出现明显的闪烁现象,这与帧动画连续平滑播放的初衷相违背。通过分析和研究后发现造成闪烁的原因在于repaint()方法。程序每一次调用repaint()方法时,就等于调用update()方法,而update( )方法所做的工作就是先消除原有画面,然后再去调用paint()方法完成重画操作。在画面被清除的过程中,整个画面会被缺
39、省的灰色填充,重画操作又将需要显示的画面覆盖掉灰色填充的画面,这样就产生了在两个连续画面之间出现灰色填充画面,这就是造成画面产生闪烁的原因。很显然,消除画面的频率越快,闪烁现象越严重。这个问题在显示速度比较慢的机器上或者重画动作比较多的情况下,常常会非常严重。,要消除该闪烁现象,最简单的方法是采用减少不必要的重画动作,重载update()方法: public void update() paint(g); 此时update()方法不进行清除Applet显示区域的操作而直接进行连续图像的绘制,从而有效避免了闪烁现象的产生。 在本程序中动画使用的图像都是一单独的文件,而当程序中遇到点阵内容很多的图
40、像需要显示时,系统实时的点阵重画需要消耗大量的时间和资源,这样仅使用重载update()方法是不能够消除闪烁现象的,此时只有使用双缓冲区技术才可完全消除闪烁现象。,双缓冲,双缓冲区是指用次画面的图像覆盖主画面的图像。其中主画面是指可见的对应屏幕的图像;次画面是指不可见的对应于主画面的尺寸相同的图像 。,双缓冲区技术,主画面的图像直接在屏幕上显示。在绘制连续动画时,首先清除次画面的内容,然后在次画面中绘制图像,等次画面中的图像绘制完成后,再将次画面的全部内容载入主画面进行显示。因为在主画面中不进行清除Applet显示区内容的操作,所以主画面在屏幕上显示时就不会产生闪烁现象。 使用双缓冲技术时,必
41、须利用creatImage()方法建立图像对象,此外还需要一个Graphics类的对象,用来将下一个要显示的画面绘制到缓冲区中,但是并不是真正地显示到屏幕上。当画面成功地绘制到缓冲区之后,再利用drawimage()方法将当前缓冲区的Image对象绘制到Applet区域中,并且迅速地覆盖前一个画面,在覆盖的过程中并不清除原来画面的背景,所以能够有效地减少闪烁的情况。也就是说把原来影像清除,显示影像的复杂工作由看不见的Image对象来进行,而在Applet上则只是单纯地将画面覆盖而已。,它的具体实现步骤如下: 1. 在init()方法内创建一张空白图像作为后台图像: Image BufferImg = creatimage(getSize( ).width , getSize( ).height); Graphics BufferG = BufferImg.getgraphics(); 2. 把原来paint()方法中的绘图动作都先转移到备用屏幕BufferG上进行操作,相当于在缓冲屏幕上绘图: BufferG.drawimage(img , x , y , this); 3. 在paint()方法内程序代码的最后
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年沅江辅警考试题库(附答案)
- 2024-2025学年度三支一扶模拟试题及完整答案详解
- 2024-2025学年度园林绿化作业人员模拟试题及答案详解【夺冠系列】
- 2024-2025学年度辅警招聘考试高频难、易错点题及答案详解(名师系列)
- 2024-2025学年度政法干警过关检测试卷及完整答案详解【考点梳理】
- 2024-2025学年度三支一扶题库附答案详解【培优】
- 2024-2025学年度执法资格经典例题含完整答案详解(名师系列)
- 2024-2025学年度三支一扶试题及答案详解【有一套】
- 2024-2025学年度政法干警考试彩蛋押题【真题汇编】附答案详解
- 2024-2025学年医疗卫生系统人员常考点试卷附答案详解(黄金题型)
- 金矿融资计划书范文
- 《成人住院患者静脉血栓栓塞症风险评估技术》
- 汽车机械基础课件:第七单元汽车液压传动课件
- 个人房屋买卖合同范本复制
- 肝的生理功能中医
- 大咯血患者急救及护理
- GB/T 44233.2-2024蓄电池和蓄电池组安装的安全要求第2部分:固定型电池
- 地质钻探施工方案
- 2024年河北省中考数学试题(含答案解析)
- Unit2Yummy!(课件)新概念英语青少版StarterA
- T-CSEM 0024-2024 智慧消防 火灾防控系统建设要求
评论
0/150
提交评论